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 Class files)
It's a good idea to keep the test classes separate from the main source code. So, they are developed, executed, and maintained separately from the production code.
Also, it avoids any possibility of running test code in the production environment.
We can follow the steps of the build tools like Maven and Gradle that look for src/test directory for test implementations.
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 developer's guide here:
Package Naming Convention
We should create a similar package structure in the src/test directory for test classes. Thus, improving the readability and maintainability of the test code.
The package of the test class should match the package of the source class whose unit of source code it'll test.
For example, the below diagram shows the standard package naming convention we can follow:
Make sure that you write each test independent of all the others
Well, whenever you write multiple Unit tests then make sure that all the Unit tests are independent of each other. This will help you to identify the root cause and test the unit of logic properly.Try to use @BeforeEach and @AfterEach annotations to set up pre-requisites if any for all your test cases. The methods annotated with @BeforeEach and @AfterEach execute before and after respectively for each method annotated with @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory in the current class.
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 the method name, in lexicographic order.
Let's demonstrate 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");
}
}
The above code will execute the test methods in the order of their names, sorted in ascending order.
Cover single specific scenario in one JUnit test case
Instead of adding multiple assertions to the same unit test, we should create separate test cases.
Of course, it's sometimes tempting to verify multiple scenarios in the same test, but it's a good idea to keep them separate. Then, in the case of test failures, it'll be easier to determine which specific scenario failed and, likewise, simpler to fix the code.
Therefore, always write a unit test to test a single specific scenario.
Use the most appropriate assertion methods
Always use proper assertions to verify the expected vs. actual results.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.
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 @BeforeEach and @AfterEach annotations
Don't initialize the object's in the constructor instead use @BeforeEach and @AfterEach annotation to set up and destroy the objects.The methods annotated with @BeforeEach and @AfterEach execute before and after respectively for each method annotated with @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory in the current class.
JUnit Test Naming Conventions
The test names should be insightful, and users should understand the behavior and expectation of the test by just glancing name itself.
I personally follow given/when/then BDD style naming conventions for Unit test names.
For example:
givenEmployeeObject_whenSaveEmployee_thenReturnSavedEmployee
givenEmployeesList_whenFindAll_thenReturnListOfEmployees
givenEmployeeObject_whenUpdateEmployee_thenReturnUpdatedEmployee
Read the below article to know more about JUnit test naming conventions:
Mock external services/dependencies
Although unit tests concentrate on specific and smaller pieces of code, there is a chance that the code is dependent on external services for some logic.
Therefore, we should mock the external services and merely test the logic and execution of our code for varying scenarios.
We can use various frameworks like Mockito, EasyMock, and JMockit for mocking external services.
For example, consider three-layer architecture in the Java project:
In the above three-layer architecture, EmployeeController depends on EmployeeService and EmailService So we have to mock EmployeeService and EmailService while Unit testing EmployeeController.
Happy Learning and Keep Coding !!!!.
Comments
Post a Comment
Leave Comment