This article includes Spring Boot, JUnit, Mockito, and Layered Architecture. It is about the Unit Test subject theoretically and practically. The Unit Test subject is the most important part of the software.
The article will begin with technical topics related to the Unit Test. After the technical part of this article, the Unit Test practices using Spring Boot, JUnit, and Mockito. The next part of this series will be about the Integration Test. And I highly recommend you read the first part of The Test Series linked below.
The word Unit in the Unit Test refers to the smallest functional part that can be tested and handled separately. We will understand it more clearly when we code unit tests.
It describes the way a system uses a particular function or feature. Use cases are used to understand, design, or test the requirements of a system. It usually includes details such as how users interact, what is expected from the system, and what results should be achieved.
It is a specific scenario where software must handle unexpected or borderline situations. Edge cases represent situations that differ from typical plans or are considered rare. These states can be used to make unexpected user logins, test limits, or uncover bugs in the system. Edge cases are often taken into account in the testing process and are used to test the robustness and stability of the system.
The Unit Test
The Unit word is explained above, The Unit Test covers all possibilities we can consider and then write. Each unit must have at least one test method. The test is not written for a method, it is written for a unit. Unit tests can be written in this order; happy paths/uses cases, edge cases, and exception situations. These steps are required, but why?
It ensures that it produces the correct outputs to the accepted inputs and exhibits the expected behavior. The Unit Test is the best for early detection of these risks and fixing bugs like unexpected situations may occur, the production code may change, the production code may not be ready for any cases, etc. Briefly, the unit test keeps the production code safe.
The other important thing about unit tests is business logic must be tested and infrastructure codes are not tested in unit tests. These could be tested in integration tests. You can examine patterns to separate business and infrastructure codes that is the Onion Architecture, Hexagonal Architecture, etc.
Another advantage of the Unit Test is fast because The Spring ApplicationContext is unnecessary when running tests. Integration tests in the same pyramid work much slower than unit tests because of the context.
Let us code!
I coded tests for the controller layer but I will not share that and other details. If you are curious about those, you can access the GitHub Repository.
Business codes are mostly found at the service layer in a layered architectural project. It means, the service layer has units and it must be tested. Let us focus on the most important part.
As you see above, there are two public methods and a private method, private methods can be thought of as part of the public methods in which they are used. And there are many possible scenarios, if you think about how many tests you need to write, the answer is enough to cover all scenarios.
Let us get to know the general annotations.
@ExtendWith is used to integrate the Mockito library into JUnit tests. @Test marks a method to provide test functionality. The test method contains the specified test case and is automatically run by the JUnit.
It is necessary to mock the dependencies of the class we are testing. As I wrote above, the reason is that The Spring ApplicationContext does not stand up and we cannot inject dependencies to the context. @Mock creates a mock dependency, and @InjectMocks injects dependencies.
@BeforeEach and @AfterEach can be used for operations that we want to do before and after each method runs.
@ParameterizedTest is used to run repetitive test cases using different parameter values. With @ValueSource we can give different parameters to the method.
Each test method consists of three main phases.
- Given: Preparation of objects required for the test case,
- When: Performing the necessary actions to run the test scenario,
- Then: Check or verify expected results.
doReturn/when determines how it behaves when a method is navigated with the specified parameters. However, dependencies are @Mock and never really go.
verify is used to check if the code under test behaves as expected and If there was a public void method, we could test it using it.
assertions are used to validate expected results.
UserServiceImpl Test class ran for 1 second 761 ms. Perfect timing! We will be comparing it with an integration test class in the next article.
You can express your opinions about this article in the comments, and you can follow me so that you do not miss my new articles.
Thanks for reading the end of the article. See you in the next articles. If you have any questions which are related to these topics, please do not hesitate to contact me via LinkedIn.