Setting up hardware and software

Tests for java programmers. Program testing, JUnit

While looking for test tasks for Java programmers, I came across an interesting site (Avast users should not go, a script Trojan is detected, others are apparently fine) - http://www.betterprogrammer.com. It tests the qualifications of Java programmers in the simplest but most automatic way: offering to write several functions (methods) of increasing complexity and copy the code into the TextArea. Next, the site engine does something with the tasks (no less than a unit test), calculates a certain qualification index based on the “speed-quality” criteria and gives the final score in the following form:

Then the questions begin. I myself programmed in Java for the second time in my life (and therefore simply skipped complex tasks), so 82% on this test correspond to the level non-java programmer. How much, then, should Java Junior, Java Programmer, and even more so Java Senior recruit?! What result can you expect from present java programmer - 90, 95, 99? And finally, what if the “programmer” scores less than 82, but nevertheless applies for some kind of job?!

I believe that software development is more than a job. I see myself as a craftsman who tries to become better every day. The "easiest" way to do this is to find some good tools and answer the following questions:

  • When should I use Tool X?
  • How should I use tool X?

Automated testing is a very important part of software development, but there are few posts on programmers' blogs about the tools used. This same article will allow you to look into my “tool box”. I'll cover 12 libraries and frameworks that I use to write unit and integration tests, and provide links to pages that will help you understand how to use them.

Let's look in my toolbox

Before you can use the tools described below, you must set up a build that automatically runs integration and unit tests. I have 2 notes on this topic:

  • Integration Testing with Maven describes how we can set up a Maven build with integration and unit tests in different directories.
  • Getting Started with Gradle: Integration Testing with the TestSets Plugin covers the same for Gradle.

Now you're ready to take a closer look at my tools. I've divided them into categories to make it easier for you to navigate.

So, here are 12 tools that I use for integration and unit testing.

Running tests

AssertJ provides a flexible API for writing assertions with useful error messages, improves the readability of test code, and allows you to turn tests into executable specifications that adhere to the desired domain-specific language.

Additionally:

  • Using Hamcrest in Testing covers how to use Hamcrest to write tests, as well as how to extend its capabilities with custom modules.
  • Turning Assertions into a Domain-Specific Language explains how to create custom assertions in AssertJ.
  • Writing Clean Tests: Replacing Assertions with Domain-Specific Language. Explains why we should replace JUnit's standard assertions with our own that use proper domain-specific language.

Data Access Code Testing

A fast database, useful for writing integration tests that run on the developer’s local machine.

An extension to JUnit that can be used to initialize the database to a known state before running each integration test and populate the database with the necessary data. DbUnit has its shortcomings, but it is a very useful tool for separating test data and test code.

Additionally:

  • describes the key components of DbUnit that you need to know to write tests using DbUnit.
  • provides five rules for writing the best tests for data access code.

JUnit- library for unit testing of Java programs. Created by Kent Beck and Eric Gamma, JUnit belongs to the xUnit family of frameworks for different programming languages, originating from Kent Beck's SUnit for Smalltalk. JUnit has spawned an ecosystem of extensions - JMock, EasyMock, DbUnit, HttpUnit, etc.

Library JUnit has been ported to other languages, including PHP (PHPUnit), C# (NUnit), Python (PyUnit), Fortran (fUnit), Delphi (DUnit), Free Pascal (FPCUnit), Perl (Test::Unit), C++ (CPPUnit) , Flex (FlexUnit), JavaScript (JSUnit).

JUnit is a Java framework for testing, i.e. testing individual sections of code, for example, methods or classes. The experience gained from working with JUnit is important in developing software testing concepts.

Example test JUnit

Import org.junit.Test; import junit.framework.Assert; public class MathTest ( @Test public void testEquals() ( Assert.assertEquals(4, 2 + 2); Assert.assertTrue(4 == 2 + 2); ) @Test public void testNotEquals() ( Assert.assertFalse(5 = = 2 + 2);

The need to use JUnit

JUnit allows you to quickly verify that your code is working at any time. If the program is not completely simple and includes many classes and methods, then testing it may take considerable time. Naturally, it is better to automate this process. Usage JUnit allows you to check the program code without significant effort and does not take much time.

Unit tests classes and functions are a kind of documentation of what is expected as a result of their execution. And not just documentation, but documentation that can automatically check the code for compliance with the provided functions. This is convenient, and often tests are developed both together and before the classes are implemented. Test-driven development is an extremely popular technology for creating serious software.

Types of testing and place of JUnit testing in the classification

Software testing can be divided into two types:

  • black box testing;
  • white box testing.

When testing a program as a black box, the internal structure of the application is not taken into account. All that matters is the functionality that the application must provide. When testing a program as a white box, the internal structure is taken into account, i.e. class and methods. In addition, testing can be divided into four levels:

  • unit tests - testing individual sections of code;
  • integration testing - testing the interaction and collaboration of components;
  • system testing - testing the entire system as a whole;
  • acceptance testing - final testing of the finished system for compliance with the requirements.

Unit testing by definition is white box testing.

Unit testing is used in two versions - JUnit 3 and JUnit 4. Let's consider both versions, since older projects still use version 3, which supports Java 1.4.

JUnit 3

To create a test, you should inherit the TestCase test class, override the setUp and tearDown methods if necessary, and, most importantly, develop test methods whose names should begin with the abbreviation “test”. When running a test, an instance of the test class is first created (for each test in the class a separate instance of the class), then the setUp method is executed, the test itself is launched, and finally the tearDown method is executed. If any of the methods throws an exception, the test fails.

Note: test methods must be public void, but can be static.

Tests consist of executing some code and checking. Checks are most often done using a class Assert although sometimes the assert keyword is used.

As an example, consider a string utility that includes methods for checking for an empty string and representing a sequence of bytes as a hexadecimal string:

Public class JUnit3StringUtilsTest extends TestCase ( private final Map toHexStringData = new HashMap(); protected void setUp() throws Exception ( toHexStringData.put("", new byte); toHexStringData.put("01020d112d7f", new byte(1,2, 13,17,45,127)); toHexStringData.put("00fff21180" , new byte(0,-1,-14,17,-128 )); //... ) protected void tearDown() throws Exception ( toHexStringData. clear(); ) public void testToHexString() ( for (Iterator iterator = toHexStringData.keySet().iterator(); iterator.hasNext();) ( final String expected = (String)iterator.next(); final byte testData = (byte)toHexStringData.get(expected); final String actual = StringUtils.toHexString(testEquals(expected, actual) ) //... )

Additional features, TestSuite

JUnit 3 has several additional features. For example, you can group tests. To do this you need to use the class TestSuite:

Public class JUnit3StringUtilsTestSuite extends TestSuite ( public JUnit3StringUtilsTestSuite() ( addTestSuite(StringUtilsJUnit3Test.class); addTestSuite(OtherTest1.class); addTestSuite(OtherTest2.class); ) )

You can repeat the test several times. RepeatedTest is used for this:

Public class JUnit3StringUtilsRepeatedTest extends RepeatedTest ( public JUnit3StringUtilsRepeatedTest() ( super(new JUnit3StringUtilsTest(), 100); ) )

By inheriting the test class from ExceptionTestCase, you can check the code for throwing an exception:

Public class JUnit3StringUtilsExceptionTest extends ExceptionTestCase ( public JUnit3StringUtilsExceptionTest(final String name) ( super(name, NullPointerException.class); ) public void testToHexString() ( StringUtils.toHexString(null); ) )

As you can see from the examples, everything is quite simple and nothing superfluous - a minimum of code for JUnit testing.

JUnit 4

JUnit 4 adds support for new features from Java 5.0; tests can be declared using annotations. At the same time, there is backward compatibility with the previous version of the framework. Almost all of the examples discussed above will work in JUnit 4 with the exception of RepeatedTest, which is missing in the new version.

What changes have been made in JUnit 4? Let's look at the same example, but using new features:

Public class JUnit4StringUtilsTest extends Assert ( private final Map toHexStringData = new HashMap (); @Before public static void setUpToHexStringData() ( toHexStringData.put("", new byte); toHexStringData.put("01020d112d7f", new byte(1,2,13,17,45,127)); toHexStringData.put("00fff21180" , new byte(0,-1,-14,17,-128)); //... ) @After public static void tearDownToHexStringData() ( toHexStringData.clear(); ) @Test public void testToHexString() ( for (Map.Entry

entry: toHexStringData.entrySet()) ( final byte testData = entry.getValue(); final String expected = entry.getKey(); final String actual = StringUtils.toHexString(testData); assertEquals(expected, actual); ) ) )

  • What's changed in JUnit 4? Assert To simplify the work, you can inherit from the class
  • , although this is not necessary. annotation@Before denotes methods that will be called before the tests are executed. Methods must be public void . Presets for the test are usually placed here, in our case this is the generation of test data (method).
  • setUpToHexStringData Annotation can be used@BeforeClass , which denotes the methods that will be called before the test class instance is created; methods should be public static void
  • , although this is not necessary. . This annotation (method) makes sense to use for testing in the case when the class contains several tests that use different presets, or when several tests use the same data, so as not to waste time creating them for each test.@After denotes methods that will be called before the tests are executed. Methods must be denotes the methods that will be called after the tests are executed. Methods must be tearDownToHexStringData).
  • , although this is not necessary. @AfterClass related in meaning to Annotation can be used, but executes methods after testing the class. As is the case with Annotation can be used, methods should be , which denotes the methods that will be called before the test class instance is created; methods should be.
  • , although this is not necessary. @Test stands for test methods. As before, these methods should be denotes methods that will be called before the tests are executed. Methods must be. The checks themselves are located here. In addition, in this annotation you can use two parameters: expected - sets the expected exception and timeout - sets the time after which the test is considered failed.

Examples of using annotations with parameters, JUnit Test:

@Test(expected = NullPointerException.class) public void testToHexStringWrong() ( StringUtils.toHexString(null); ) @Test(timeout = 1000) public void infinity() ( while (true); )

Ignoring test execution, JUnit Ignore

If one of the tests needs to be disabled for some serious reason, for example, the test constantly fails. Test correction can be deferred until a brighter future by annotating @Ignore. If you place this annotation on a class, then all tests in that class will be disabled.

@Ignore @Test(timeout = 1000) public void infinity() ( while (true); )

Testing rules, JUnit Rule

JUnit allows you to use developer-defined rules before and after test execution, which expand functionality. For example, there are built-in rules for setting a timeout for a test (Timeout), for setting expected exceptions (ExpectedException), for working with temporary files (TemporaryFolder), etc.

To declare a rule you need to create public Not static a type field derived from MethodRule and annotate it with a keyword Rule.

Public class JUnitOtherTest ( @Rule public final TemporaryFolder folder = new TemporaryFolder(); @Rule public final Timeout timeout = new Timeout(1000); @Rule public final ExpectedException thrown = ExpectedException.none(); @Ignore @Test public void anotherInfinity( ) ( while (true); ) @Test public void testFileWriting() throws IOException ( final File log = folder.newFile("debug.log"); final FileWriter logWriter = new FileWriter(log); logWriter.append("Hello, "); logWriter.append("World!!!"); logWriter.flush(); logWriter.close(); ) @Test public void testExpectedException() throws IOException ( thrown.expect(NullPointerException.class); StringUtils.toHexString (null) )

Test Suites, JUnit Suite, SuiteClasses

Test run can be configured using annotation @RunWith. Test classes, which contain test methods, can be combined into test suites (Suites). For example, two object testing classes have been created: TestFilter, TestConnect. These two test classes can be combined into one test class TestWidgets.java:

Package com.objects; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses (( TestFilter.class, TestConnect.class )) public class TestWidgets ()

To configure the tests to be run, the @SuiteClasses annotation is used, which includes test classes.

Abstract Categories

, although this is not necessary. Categories allows you to combine tests into categories (groups). For this purpose, the test defines a category @Category, after which the test categories to run in the Suite are configured. It might look like this:

Public class JUnitStringUtilsCategoriesTest extends Assert ( //... @Category (Unit.class) @Test public void testIsEmpty() ( //... ) //... ) @RunWith (Categories.class) @Categories.IncludeCategory ( Unit.class) @Suite.SuiteClasses (( JUnitOtherTest.class, JUnitStringUtilsCategoriesTest.class )) public class JUnitTestSuite()

Abstract, JUnit Parameterized

, although this is not necessary. Parameterized allows the use of parameterized tests. To do this, a static method is declared in the test class, returning a list of data that will be used as arguments to the class constructor.

@RunWith (Parameterized.class) public class JUnitStringUtilsParameterizedTest extends Assert ( private final CharSequence testData; private final boolean expected; public JUnitStringUtilsParameterizedTest(final CharSequence testData, final boolean expected) ( this.testData = testData; this.expected = expected; ) @Test public void testIsEmpty () ( final boolean actual = StringUtils.isEmpty (testData); assertEquals(expected, actual); ) @Parameterized.Parameters public static List isEmptyData() ( return Arrays.asList(new Object ( ( null, true ), ( "", true ), ( " ", false ), ( "some string", false ), )); ) )

Method parameterization: Theories.class, DataPoints, DataPoint, Theory

, although this is not necessary. Theories parameterizes the test method, not the constructor. Data is marked with @DataPoints And @DataPoint, test method - using @Theory. A test using this functionality might look something like this:

@RunWith(Theories.class) public class JUnitStringUtilsTheoryTest extends Assert ( @DataPoints public static Object isEmptyData = new Object ( ( "", true ), ( " ", false ), ( "some string", false ), ); @DataPoint public static Object nullData = new Object ( null, true ); @Theory public void testEmpty(final Object... testData) ( final boolean actual = StringUtils.isEmpty ((CharSequence) testData); assertEquals(testData, actual); ) )

Test execution order

If you need to run tests in a specific order, you can use the @FixMethodOrder(MethodSorters.NAME_ASCENDING) annotation defined in JUnit 4.11. For example:

@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class MyTest ( @Test public void test01())(...) @Test public void test02())(...) ... @Test public void test09())(...) )

Otherwise, you can use the following 2 approaches.

Void test01(); void test02(); ... void test09(); @Test public void testOrder1() ( test1(); test3(); ) @Test(expected = Exception.class) public void testOrder2() ( test2(); test3(); test1(); ) @Test(expected = NullPointerException.class) public void testOrder3() ( test3(); test1(); test2(); )

@Test public void testAllOrders() ( for (Object sample: permute(1, 2, 3)) ( for (Object index: sample) ( switch (((Integer) index).intValue()) ( case 1: test1( ); break; case 2: test2(); break;

List of main annotations

annotationDescription
@Test
public void testMethod()
the method is test
@Test(timeout=100)
public void testMethod()
if the execution time exceeds the timeout parameter, the test will fail
@Test (expected = MyException.class)
public void testMethod()
the method must throw an exception belonging to the MyException class, otherwise the test will fail
@Ignore
public void testMethod()
ignore test method
@BeforeClass
a method called once for a class before executing test methods; here you can place an initialization that needs to be performed only once, for example, reading data that will be used in test methods or creating a connection to the database
@AfterClass
public static void testMethod()
a method called once for a class after executing test methods; here you can place a deinitialization that needs to be done only once, for example, closing a connection to the database or deleting data that is no longer needed
@Before
public static void beforeMethod()
a method called before each test method in the test class; here you can perform the necessary initialization, for example, set the initial parameters
@After
public static void afterMethod()
a method called after each test method in the test class; here you can perform the necessary deinitialization, for example, delete data that is no longer needed

List of Asserts check types

Type of checkDescription
fail()
fail(String message)
aborting the test with an error, i.e. the test will fail
assertTrue(boolean condition)
assertTrue(java.lang.String message, boolean condition)
checking if condition is equal to true
assertFalse(boolean condition)
assertFalse(String message, boolean condition)
checking if condition is equal to false
assertEquals(<тип>expected<тип>actual)
assertEquals(String message,<тип>expected<тип>actual)
equality check;<тип>- these are Object, int, double, etc.
assertArrayEquals(byte expecteds, byte actuals)
assertArrayEquals(String message,<тип>expecteds<тип>actuals)
checking arrays for equality; similar to assertEquals;<тип>- these are Object, int, double, etc.
assertNotNull(Object object)
assertNotNull(String message, Object object)
checking that Object is not null
assertNull(Object object)
assertNull(String message, Object object)
checking that Object is null
assertSame(Object expected, Object actual)
assertSame(String message, Object expected, Object actual)
checking for equality of two objects expected and actual, i.e. the same object

JUnit testing example

To demonstrate the main capabilities of JUnit, we use the primitive java class FuncMath, which has two methods - finding the factorial of a non-negative number and the sum of two numbers. In addition, the class instance will contain a method call counter.

Public class FuncMath ( int calls; public int getCalls() ( return calls; ) public long factorial(int number) ( calls++; if (number< 0) throw new IllegalArgumentException(); long result = 1; if (number >1) ( for (int i = 1; i< = number; i++) result = result * i; } return result; } public long plus(int num1, int num2) { calls++; return num1 + num2; } }

Sometimes some context is needed to execute each test case, such as pre-created class instances. And after execution, you need to release the reserved resources. In this case, the @Before and @After annotations are used. The method marked @Before will be executed before each test case, and the method marked @After will be executed after each test case. If initialization and release of resources needs to be done only once - respectively, before and after all tests - then use a pair of annotations @BeforeClass and @AfterClass.

A test class with several scenarios will look like this:

Import org.junit.Test; import org.junit.After; import org.junit.Before; import org.junit.Assert; import org.junit.AfterClass; import org.junit.BeforeClass; public class JUnit_funcMath extends Assert ( private FuncMath math; @Before public void init() ( math = new FuncMath(); ) @After public void tearDown() ( math = null; ) @Test public void calls() ( assertEquals(" math.getCalls() != 0", 0, dao.getConnection()); math.factorial(1); assertEquals(1, math.getCalls()); math.factorial(1); assertEquals(2, math. getCalls()); ) @Test public void factorial() ( assertTrue(math.factorial(0) == 1); assertTrue(math.factorial(1) == 1); assertTrue(math.factorial(5) == 120); ) @Test(expected = IllegalArgumentException.class) public void factorialNegative() ( math.factorial(-1); ) @Ignore @Test public void todo() ( assertTrue(math.plus(1, 1) == 3); ) )

The calls method tests the validity of the call counter. The factorial method checks whether the factorial is calculated correctly for some standard values. The factorialNegative method checks that an IllegalArgumentException will be thrown for negative factorial values. The todo method will be ignored.

In conclusion, it should be noted that not all possibilities for using JUnit are presented in the article. But as you can see from the examples given, the framework is quite easy to use, there are few additional features, but it can be expanded using rules and launchers.

Testing is not always fun and interesting.

This process is usually quite lengthy and sometimes full of monotonous work.

It seems that just recently programmers used standard output or a debugger to test java classes.

In this article I will describe the JUnit 4 library, which greatly simplifies and automates the process of writing tests.

Now let's write Unit tests. To do this, we will create a class with a number of test methods.

Naturally, a class can also contain ordinary auxiliary methods.

  • In order for the test runner to determine who is who, test methods must be annotated with @Test.
  • An annotation can have the following parameters:

expected - indicate which exception will be generated by the method (see example below);

timeout - after what time in milliseconds the test will stop running and count it as unsuccessful.

If you want to indicate that a certain test should be skipped, then mark it with the @Ignore annotation. Although you can simply remove the @Test annotation.

There may be times when you need some context, such as pre-created class instances, to run each test case. And after execution, you need to release the reserved resources. In this case you will need @Before and @After annotations. The method marked @Before will be executed before each test case, and the method marked @After will be executed after each test case.

If initialization and release of resources needs to be done only once - respectively, before and after all tests - then use a pair of annotations @BeforeClass and @AfterClass.

And here is the test class itself with several test scripts:

Public class MathFuncTest ( private MathFunc math; @Before public void init() ( math = new MathFunc(); ) @After public void tearDown() ( math = null; ) @Test public void calls() ( assertEquals(0, math .getCalls()); math.factorial(1); assertEquals(1, math.getCalls()); assertEquals(2, math.getCalls()); ( assertTrue(math.factorial(0) == 1); assertTrue(math.factorial(1) == 1); assertTrue(math.factorial(5) == 120); ) @Test(expected = IllegalArgumentException.class) public void factorialNegative() ( math.factorial(-1); ) @Ignore @Test public void todo() ( assertTrue(math.plus(1, 1) == 3); ) )

  • assertEquals - the expected result and the received result match;
  • assertNull - the result of the expression is null;
  • assertNotNull - the result of the expression is different from null;
  • assertSame - the expected and received objects are the same object.
  • fail - the method generates an AssertionError exception - we add it where the program execution should not go.

In our modern world, IDEs are able to find and simply run tests in a project.

But what if you want to run them manually using program code.

To do this, you can use Runner. There are text versions - junit.textui.TestRunner, graphic versions - junit.swingui.TestRunner, junit.awtui.TestRunner.

But a slightly more modern method is to use the JUnitCore class. Add the following main method to the MathFuncTest class:

Public static void main(String args) throws Exception ( JUnitCore runner = new JUnitCore(); Result result = runner.run(MathFuncTest.class); System.out.println("run tests: " + result.getRunCount()); System.out.println("failed tests: " + result.getFailureCount()); System.out.println("ignored tests: " + result.getIgnoreCount()); System.out.println("success: " + result .wasSuccessful());

And the execution result:

Run tests: 3 failed tests: 0 ignored tests: 1 success: true

In earlier versions of JUnit, to write a test class, you had to create a descendant of junit.framework.TestCase.

Nowadays, Java is used in all areas of software development - server applications, desktop applications, tools, web applications - all of these are developed using Java and related technologies.

Target audience of the Java test

The Java test contains questions that most developers with minimal Java programming experience will know the answers to. The test will be useful to many, including people who do not have much experience with Java and want to gain a minimal understanding of the basic concepts of this popular programming language. This test can also be useful for students studying Java to consolidate knowledge acquired at universities or as a result of independent work.

Java Test Prerequisites

It is assumed that before taking the Java Fundamentals test, you already know the basic concepts such as “variable”, “loop”, “method” and know the syntax of basic language constructs. If this is not the case, we recommend that you first familiarize yourself with the basics of Java by reading an introductory course.

Java Test Structure

The Java - Fundamentals test covers the following topics:

You can read more about the topics of the test questions below:

  • Objects and classes: questions in this topic cover concepts such as a class, an instance of a class (object), constructors, methods and class fields.
  • Syntax: Questions in this section will test your knowledge of language keywords, operators, basic types (primitives, String), control constructs (if-else, while, for), and understanding of exception handling (Exception).
  • Other: This section contains questions regarding basic concepts, terms and abbreviations, naming conventions for classes, variables and methods. This section also covers questions about files (.java, .class, .jar) and packages, the life cycle of objects, and garbage collection.

Taking an online Java test helps you test and strengthen your knowledge of Java fundamentals, and also leads you to take more advanced tests from the Java tests section.

We hope that you find this test useful and entertaining.

Further development of the Java test

The Java test is constantly updated with new questions.

The Quizful team's immediate plans include creating expert-level tests in Java. Perhaps a Java Puzzlers test will be created, but unfortunately it is very slowly filled with questions.

If you have any comments about the test questions, be sure to write to us. Good luck.

Did you like the article? Share with your friends!
Was this article helpful?
Yes
No
Thanks for your feedback!
Something went wrong and your vote was not counted.
Thank you. Your message has been sent
Found an error in the text?
Select it, click Ctrl + Enter and we will fix everything!