JUnit Framework Best Practices

In this article, we will discuss the JUnit Framework best practices. This article belongs to my favorite Java Best Practices Series category.

How to organize Tests(JUnit Java files)

The better way is to place the tests in a separate parallel directory structure with package alignment. 
Example:
── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── javadevelopersguide
    │   │           └── junit
    │   │               └── Calculator.java
    │   ├── resources
    └── test
        ├── java
        │   └── com
        │       └── javadevelopersguide
        │           └── junit
        │               └── CalculatorTest.java
        └── resources
If you are new to JUnit Framework then we suggest you walk-through our top JUnit developers guide here:

Don't assume the order in which tests within a test case run

By default, JUnit executes tests in any order. To change the test execution order simply annotate your test class using @FixMethodOrder and specify one of the available MethodSorters:
@FixMethodOrder(MethodSorters.JVM): Leaves the test methods in the order returned by the JVM. This order may vary from run to run.
@FixMethodOrder(MethodSorters.NAME_ASCENDING): Sorts the test methods by method name, in lexicographic order.
Let's demonstrates this best practice by a simple example.
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestMethodOrder {

    @Test
    public void testA() {
        System.out.println("first");
    }
    @Test
    public void testB() {
        System.out.println("second");
    }
    @Test
    public void testC() {
        System.out.println("third");
    }
}
Above code will execute the test methods in the order of their names, sorted in ascending order.

Do not skip unit tests

Don't skip unit tests, instead of skipping unit tests, remove them from source control.
There are a number of ways to skip unit tests, but you shouldn't use any of them:
  • Do not use JUnit's @Ignore annotation.
  • Do not use Maven's maven.test.skip property.
  • Do not use the Maven Surefire Plugin's skipTests property.
  • Do not use the Maven Surefire Plugin's excludes property.
You can disable the test using JUnit 5 Disabled Annotation.

Each unit test method should perform exactly one assertion

There are two reasons to aim for each unit test to have exactly one assertion:
  • When a unit test fails, it is much easier to determine what went wrong. If a failed unit test has three assertions, further effort is required to determine which one failed. If a failed unit test has only one assertion, no such effort is required.
  • When a unit test performs more than one assertion, it is not guaranteed that all of the assertions occur. In particular, if an unchecked exception occurs, the assertions after the exception do not happen; instead, JUnit marks the unit test method as having an error and proceeds to the next test method. Therefore, strive for one assertion per test method.

Use the most appropriate assertion methods

JUnit has many assertion methods - assertEquals, assertTrue, assertFalse, assertNull, assertNotNull, assertArrayEquals, assertSame. Know the assertions in the latest version of JUnit and use the most appropriate one to have the most readable test code. 
Check out my up-to-date version of JUnit Framework JUnit 5 Developer Guide.

Put assertion parameters in the proper order

Sometimes we do small mistakes like passing the parameters to asserts. The parameters to JUnit's assertions are:
expected
actual
For example, use assertEquals(expected, actual) rather than assertEquals(actual, expected). Ordering the parameters correctly ensures that JUnit's messages are accurate.

Use @Before and @After annotations

Don't initialize the object's in the constructor instead use @Before and @After annotation to set up and destroy the objects.
Check out JUnit 5 Standard Test Class Basic Template. You can copy/paste this template and edit these templates to suit your coding style.

JUnit Test Naming Conventions

Developers prefer different naming Conventions to name their test cases. I Personal prefer below approach and follow in my day to day project work: 
Name test class using this formula:
[The name of the tested class]Test
For examples:
UserServiceImplTest
UserRepositoyTest
CustomerDaoTest
The benefit of this approach is that if a test fails, this rule helps us figure out which class is broken without reading the test code.
To name test methods I prefer GivenWhenThen
  • The given part describes the state of the world before you begin the behavior you're specifying in this scenario. You can think of it as the pre-conditions to the test.
  • The when section is that behavior that you're specifying.
  • Finally, the then section describes the changes you expect due to the specified behavior. Formula:
Given_Preconditions_When_StateUnderTest_Then_ExpectedBehavior
Example:
Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail
whenTaskSubmitted_ThenFutureResultObtained()
whenUsingAgeComparator_thenSortedList()
There are many other ways to name the Test Classes and test methods. 
Refer these articles for more reference:

Create unit tests that target exceptions

If some of your test cases, which expect the exceptions to be thrown from an application, use “expected” attribute like this. try avoiding catching exception in catch block and using fail/ or asset method to conclude the test.
@Test(expected=SomeDomainSpecificException.SubException.class)
Read more on JUnit 4 Exception Testing Example. JUnit 5 provides assertThrows and expectThrows annotations to test the exception handling in the JUnit framework - JUnit 5 Exception Testing with Example.

Use JUnit 5 Features

JUnit 5 provides many additional features to JUnit 4.

Nested Tests

Nested tests have been added to allow developers to express complex relationships between different groups of tests.
Read more on JUnit 5 Nested Tests Example

Disabling Tests

We use @Ignore annotation is used to disable or ignore the tests in JUnit 4. JUnit 5 provides a @Disabled annotation to disable test class or test methods.

Display names for Test Classes

JUnit 5 provides a feature to display names for Test Classes, test methods, Test Suites and display technical names of the Test Classes
Read more on JUnit 5 Display Names Example

JUnit 5 Repeated Tests

It provides us with a powerful way to write any test that we want to repeat several times.

Aggregating Tests in Suites

JUnit framework provides facility to run multiple Test Classes at a time using Suite as a runner. We can combine the test classes by a feature of modules and run respective Test Classes using Suite Runner.
For Example:
package com.developersguide.junit.suites;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
  LoginServiceTest.class,
  UserServiceTest.class,
})
public class SuiteTest {

}

Migrate from JUnit 4 to JUnit 5

I believe that maintaining libraries up-to-date is very important.

The advantage of JUnit 5 over JUnit 4

  • The entire JUnit 4 framework was contained in a single jar library. The whole library needs to be imported even when only a particular feature is required. In JUnit 5, we get more granularity and can import only what is necessary
  • One test runner can only execute tests in JUnit 4 at a time (e.g. SpringJUnit4ClassRunner or Parameterized ). JUnit 5 allows multiple runners to work simultaneously
  • JUnit 4 never advanced beyond Java 7, missing out on a lot of features from Java 8. JUnit 5 makes good use of Java 8 features The idea behind JUnit 5 was to completely rewrite JUnit 4 to solve most of these drawbacks
Read more about migration from JUnit 4 to JUnit 5 on http://www.baeldung.com/junit-5-migration

Other JUnit Framework Best Practices(Good to know)

  • Ensure that test code is separated from production code
  • Do not print anything out in unit tests
  • Do not use static members in a test class. If you have used then re-initialize for each test case
  • All methods, regardless of visibility, should have appropriate unit tests
  • Write tests for methods that have the fewest dependencies first, and work your way up
There are few sites which provide a good idea about writing better JUnit test cases:

JUnit framework basic templates(JUnit 4 & 5)

If you are using JUnit 4 dependency in your projects then use this standard Basic Test Template. Copy/paste and edit these templates to suit your coding style.
package junit;
 
import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;
 
public class SimpleTest {
 
    private Collection collection;
    
    @BeforeClass
    public static void oneTimeSetUp() {
        // one-time initialization code        
    }
 
    @AfterClass
    public static void oneTimeTearDown() {
        // one-time cleanup code
    }
 
    @Before
    public void setUp() {
        collection = new ArrayList();
    }
    
    @After
    public void tearDown() {
        collection.clear();
    }
 
    @Test
    public void testEmptyCollection() {
        assertTrue(collection.isEmpty());
    }
    
    @Test
    public void testOneItemCollection() {
        collection.add("itemA");
        assertEquals(1, collection.size());
    }
}
If you are using JUnit 5 dependency in your projects then use this standard Basic Test Template. Copy/paste and edit these templates to suit your coding style.
import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class StandardTests {

    @BeforeAll
    static void initAll() {
    }

    @BeforeEach
    void init() {
    }

    @Test
    void succeedingTest() {
    }

    @Test
    void failingTest() {
        fail("a failing test");
    }

    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }

    @AfterEach
    void tearDown() {
    }

    @AfterAll
    static void tearDownAll() {
    }
}
Please write comments, if you want to give any suggestions or feedback about this article would be appreciated.

If you like my articles and want similar kind of stuff, connect with me on Google +,  FacebookTwitter,  GitHub, and StackOverflow.


Check out our top viewed Java Guides and Java/J2EE Best practices. I hope you like it.

Happy Learning and Keep Coding !!!!.

Comments