This tutorial explains unit testing with JUnit 4.x. It explains the creation of JUnit tests and the usage of the Eclipse IDE for developing software tests.
For further information see:
2. Using JUnit
2.2. How to define a test in JUnit?
A JUnit test is a method contained in a class which is only used for testing.
This is called a Test class.
To define that a certain method is a test method, annotate it with the @Test
annotation.
This method executes the code under test. You use an assert method, provided by JUnit or another assert framework, to check an expected result versus the actual result. These method calls are typically called asserts or assert statements.
You should provide meaningful messages in assert statements. That makes it easier for the user to identify and fix the problem. This is especially true if someone looks at the problem, who did not write the code under test or the test code.
2.3. Example JUnit test
The following code shows a JUnit test using the JUnit 4 version.
Assume you have this class which you want to test.
package com.vogella.junit4;
public class Calculator {
public int multiply(int a, int b) {
return a * b;
}
}
A test class containing software tests for the above class could look like the following.
package com.vogella.junit4;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
public class CalculatorTest {
private Calculator calculator;
@Before
void setUp() throws Exception {
calculator = new Calculator();
}
@Test
void testMultiply() {
assertEquals( "Regular multiplication should work", calculator.multiply(4,5), 20);
}
@Test
void testMultiplyWithZero() {
assertEquals("Multiple with zero should be zero",0, calculator.multiply(0,5));
assertEquals("Multiple with zero should be zero", 0, calculator.multiply(5,0));
}
}
2.4. JUnit naming conventions
There are several potential naming conventions for JUnit tests. A widely-used solution for classes is to use the "Test" suffix at the end of test classes names.
As a general rule, a test name should explain what the test does. If that is done correctly, reading the actual implementation can be avoided.
One possible convention is to use the "should" in the test method name. For example, "ordersShouldBeCreated" or "menuShouldGetActive". This gives a hint what should happen if the test method is executed.
Another approach is to use "Given[ExplainYourInput]When[WhatIsDone]Then[ExpectedResult]" for the display name of the test method.
2.5. JUnit naming conventions for Maven
If you are using the Maven build system, you should use the "Test" suffix for test classes. The Maven build system (via its surfire plug-in) automatically includes such classes in its test scope.
This class can be executed like any other Java program on the command line. You only need to add the JUnit library JAR file to the classpath.
3. Using JUnit 4
3.1. Defining test methods
JUnit uses annotations to mark methods as test methods and to configure them. The following table gives an overview of the most important annotations in JUnit for the 4.x and 5.x versions. All these annotations can be used on methods.
JUnit 4 | Description |
---|---|
|
Import statement for using the following annotations. |
|
Identifies a method as a test method. |
|
Executed before each test. It is used to prepare the test environment (e.g., read input data, initialize the class). |
|
Executed after each test. It is used to cleanup the test environment (e.g., delete temporary data, restore defaults). It can also save memory by cleaning up expensive memory structures. |
|
Executed once, before the start of all tests.
It is used to perform time intensive activities, for example, to connect to a database. Methods marked with this annotation need to be defined as |
|
Executed once, after all tests have been finished.
It is used to perform clean-up activities, for example, to disconnect from a database. Methods annotated with this annotation need to be defined as |
|
Marks that the test should be disabled. This is useful when the underlying code has been changed and the test case has not yet been adapted. Or if the execution time of this test is too long to be included. It is best practice to provide the optional description, why the test is disabled. |
|
Fails if the method does not throw the named exception. |
|
Fails if the method takes longer than 100 milliseconds. |
3.2. Assert statements
JUnit provides static methods to test for certain conditions via the Assert
class.
These assert statements typically start with assert
.
They allow you to specify the error message, the expected and the actual result.
An assertion method compares the actual value returned by a test to the expected value.
It throws an AssertionException
if the comparison fails.
The following table gives an overview of these methods. Parameters in [] brackets are optional and of type String.
Statement | Description |
---|---|
fail([message]) |
Let the method fail. Might be used to check that a certain part of the code is not reached or to have a failing test before the test code is implemented. The message parameter is optional. |
assertTrue([message,] boolean condition) |
Checks that the boolean condition is true. |
assertFalse([message,] boolean condition) |
Checks that the boolean condition is false. |
assertEquals([message,] expected, actual) |
Tests that two values are the same. Note: for arrays the reference is checked not the content of the arrays. |
assertEquals([message,] expected, actual, tolerance) |
Test that float or double values match. The tolerance is the number of decimals which must be the same. |
assertNull([message,] object) |
Checks that the object is null. |
assertNotNull([message,] object) |
Checks that the object is not null. |
assertSame([message,] expected, actual) |
Checks that both variables refer to the same object. |
assertNotSame([message,] expected, actual) |
Checks that both variables refer to different objects. |
3.3. JUnit test suites
If you have several test classes, you can combine them into a test suite. Running a test suite executes all test classes in that suite in the specified order. A test suite can also contain other test suites.
The following example code demonstrates the usage of a test suite.
It contains two test classes (MyClassTest and MySecondClassTest).
If you want to add another test class, you can add it to the @Suite.SuiteClasses
statement.
package com.vogella.junit.first;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
MyClassTest.class,
MySecondClassTest.class })
public class AllTests {
}
3.4. Disabling tests
The @Ignore annotation allow to statically ignore a test.
Alternatively you can use Assume.assumeFalse
or Assume.assumeTrue
to define a condition for the test.
Assume.assumeFalse
marks the test as invalid, if its condition evaluates to true.
Assume.assumeTrue
evaluates the test as invalid if its condition evaluates to false.
For example, the following disables a test on Linux:
Assume.assumeFalse(System.getProperty("os.name").contains("Linux"));
3.5. Parameterized test
JUnit allows you to use parameters in a tests class. This class can contain one test method and this method is executed with the different parameters provided.
You mark a test class as a parameterized test with the @RunWith(Parameterized.class)
annotation.
Such a test class must contain a static method annotated with the @Parameters
annotation.
That method generates and returns a collection of arrays.
Each item in this collection is used as parameter for the test method.
You can use the @Parameter
annotation on public fields to get the test values injected in the test.
The following code shows an example for a parameterized test.
It tests the multiply()
method of the MyClass
class which is included as inner class for the purpose of this example.
package testing;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.Assert.assertEquals;
import static org.junit.runners.Parameterized.*;
@RunWith(Parameterized.class)
public class ParameterizedTestFields {
// fields used together with @Parameter must be public
@Parameter(0)
public int m1;
@Parameter(1)
public int m2;
@Parameter(2)
public int result;
// creates the test data
@Parameters
public static Collection<Object[]> data() {
Object[][] data = new Object[][] { { 1 , 2, 2 }, { 5, 3, 15 }, { 121, 4, 484 } };
return Arrays.asList(data);
}
@Test
public void testMultiplyException() {
MyClass tester = new MyClass();
assertEquals("Result", result, tester.multiply(m1, m2));
}
// class to be tested
class MyClass {
public int multiply(int i, int j) {
return i *j;
}
}
}
Alternatively to using the @Parameter
annotation you can use a constructor in which you store the values for each test.
The number of elements in each array
provided by
the
method annotated with
@Parameters
must
correspond to the number of parameters in the
constructor of the
class.
The class is created for each parameter and
the test
values are
passed
via the constructor to the class.
package de.vogella.junit.first;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ParameterizedTestUsingConstructor {
private int m1;
private int m2;
public ParameterizedTestUsingConstructor(int p1, int p2) {
m1 = p1;
m2 = p2;
}
// creates the test data
@Parameters
public static Collection<Object[]> data() {
Object[][] data = new Object[][] { { 1 , 2 }, { 5, 3 }, { 121, 4 } };
return Arrays.asList(data);
}
@Test
public void testMultiplyException() {
MyClass tester = new MyClass();
assertEquals("Result", m1 * m2, tester.multiply(m1, m2));
}
// class to be tested
class MyClass {
public int multiply(int i, int j) {
return i *j;
}
}
}
If you run this test class, the test method is executed with each defined parameter. In the above example the test method is executed three times.
A more flexible and easier to write approach is provided by the JUnitParams from https://github.com/Pragmatists/JUnitParams.
3.6. JUnit Rules
Via JUnit rules you can add behavior to each tests in a test class.
You can annotate fields of type TestRule
with the @Rule
annotation.
You can create objects which can be used and configured in your test methods.
This adds more flexibility to your tests.
You could, for example, specify which exception message you expect during the execution of your test code.
package de.vogella.junit.first;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class RuleExceptionTesterExample {
@Rule
public ExpectedException exception = ExpectedException.none();
@Test
public void throwsIllegalArgumentExceptionIfIconIsNull() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Negative value not allowed");
ClassToBeTested t = new ClassToBeTested();
t.methodToBeTest(-1);
}
}
JUnit already provides several useful rule implementations.
For example, the TemporaryFolder
class allows to setup files and folders which are automatically removed after each test run.
The following code shows an example for the usage of the TemporaryFolder
implementation.
package de.vogella.junit.first;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class RuleTester {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Test
public void testUsingTempFolder() throws IOException {
File createdFolder = folder.newFolder("newfolder");
File createdFile = folder.newFile("myfilefile.txt");
assertTrue(createdFile.exists());
}
}
For more examples of existing rules see https://github.com/junit-team/junit4/wiki/Rules.
3.7. Writing custom JUnit rules
To write your custom rule, you need to implement the TestRule
interface.
This interface defines the apply(Statement, Description)
method which must return an instance of Statement
.
Statement represent the tests within the JUnit runtime and Statement#evaluate() run these.
Description describes the individual test.
It allows to read information about the test via reflection.
The following is a simple example for adding a log statement to an Android application before and after test execution.
package testing.android.vogella.com.asynctask;
import android.util.Log;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class MyCustomRule implements TestRule {
private Statement base;
private Description description;
@Override
public Statement apply(Statement base, Description description) {
this.base = base;
this.description = description;
return new MyStatement(base);
}
public class MyStatement extends Statement {
private final Statement base;
public MyStatement(Statement base) {
this.base = base;
}
@Override
public void evaluate() throws Throwable {
System.
Log.w("MyCustomRule",description.getMethodName() + "Started" );
try {
base.evaluate();
} finally {
Log.w("MyCustomRule",description.getMethodName() + "Finished");
}
}
}
}
To use this rule, simple add a field annotated with @Rule
to your test class.
@Rule
public MyCustomRule myRule = new MyCustomRule();
3.8. Categories
It is possible to define categories of tests and include or exclude them based on annotations. The following example is based on the JUnit 4.8 release notes.
public interface FastTests { /* category marker */
}
public interface SlowTests { /* category marker */
}
public class A {
@Test
public void a() {
fail();
}
@Category(SlowTests.class)
@Test
public void b() {
}
}
@Category({ SlowTests.class, FastTests.class })
public class B {
@Test
public void c() {
}
}
@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@SuiteClasses({ A.class, B.class })
// Note that Categories is a kind of Suite
public class SlowTestSuite {
// Will run A.b and B.c, but not A.a
}
@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@ExcludeCategory(FastTests.class)
@SuiteClasses({ A.class, B.class })
// Note that Categories is a kind of Suite
public class SlowTestSuite {
// Will run A.b, but not A.a or B.c
}
3.9. JUnit static imports
Static import is a feature that allows fields and methods defined in a class as public static
to be used without specifying the class in which the field is defined.
JUnit assert statements are typically defined as public static
to allow the developer to write short test statements.
The following snippet demonstrates an assert statement with and without static imports.
// without static imports you have to write the following statement
Assert.assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5));
// alternatively define assertEquals as static import
import static org.junit.Assert.assertEquals;
// more code
// use assertEquals directly because of the static import
assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5));
4. Installation of JUnit
4.1. Using JUnit with Gradle
To use JUnit in your Gradle build, add a testCompile dependency to your build file.
apply plugin: 'java'
dependencies {
testImplementation 'junit:junit:4.12'
}
4.2. Using JUnit with Maven
To use JUnit in your Maven build, add the following dependency to your pom file.
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
4.3. Using JUnit without Maven or Gradle
The Eclipse IDE ships with a version of JUnit.
It is also possible to download the JUnit library explicitly from the JUnit website. Add this library to your classpath, to use it.
5. Exercise: Using JUnit
5.1. Project preparation
Create a new project called com.vogella.junit.first. Create a new source folder test. For this right-click on your project, select Properties and choose
. Select the Source tab.Press the Add Folder button. Afterwards, press the Create New Folder button. Enter test as folder name.
The result is depicted in the following screenshot.
NOTE:You can also add a new source folder by right-clicking on a project and selecting
.5.2. Create a Java class
In the src folder, create the com.vogella.junit.first
package and the following class.
package com.vogella.junit.first;
public class MyClass {
public int multiply(int x, int y) {
// the following is just an example
if (x > 999) {
throw new IllegalArgumentException("X should be less than 1000");
}
return x / y;
}
}
5.3. Create a JUnit test
Right-click on your new class in the Package Explorer view and select
.In the following wizard ensure that the New JUnit 4 test flag is selected and set the source folder to test, so that your test class gets created in this folder.
Press the Next button and select the methods that you want to test.
If the JUnit library is not part of the classpath of your project, Eclipse prompts you to add it. Use this to add JUnit to your project.
Create a test with the following code.
package com.vogella.junit.first;
import static org.junit.Assert.assertEquals;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class MyClassTest {
@Test(expected = IllegalArgumentException.class)
public void testExceptionIsThrown() {
MyClass tester = new MyClass();
tester.multiply(1000, 5);
}
@Test
public void testMultiply() {
MyClass tester = new MyClass();
assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5));
}
}
5.4. Run your test in Eclipse
Right-click on your new test class and select
.The result of the tests are displayed in the JUnit view. In our example one test should be successful and one test should show an error. This error is indicated by a red bar.
The test is failing, because our multiplier class is currently not working correctly. It does a division instead of multiplication. Fix the bug and re-run the test to get a green bar.
6. JUnit Resources
6.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting