Let’s get started with Java Testing Framework : JUnit 5
The higher your test coverage, the less code failures.
You may not write successful production code until you have written a failing unit test
Catching mistakes in the development phase is a lot less costly than finding them in production. It not only saves you from embarrassment, but it also saves your company and user from frustration.
The reason is simple. If you have tests, you do not fear making changes to the existing code! Without tests, every change is a possible bug.
No matter how flexible your architecture is!
No matter how nicely partitioned your design!
Without tests, you will be reluctant to make changes because of the fear that you will introduce undetected bugs.
Stick to the Basics : What is Unit testing ?
Unit test is at the lowest level and typically checks a single method to confirm that they returns the intended result based on condition
Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the “unit”) meets its design and behaves as intended.
So, we can explain that Unit Testing is to make sure how well our code or every unit on our source code runs, based on the business process which is already defined.
Lets make our hand dirty to write Clean Test Cases :
First thing First : Adding JUnit 5 dependencies
- The
junit-jupiter-api
provides the public API for writing tests cases
Problem Statement:
Find the age as per given birth date (Day, Month, Year).
Constraint :
Birth date should be before today’s date
To write a simple JUnit test case, we need to declare a class in test package and implement a methods with@Test
annotation
The return type of test method is always void and you need to verify expected and actual results to make the test pass or fail.
Test Case 1: Success Scenario
Input: Birth Date : 15th Aug, 1992
Expected output Age : 29 years
Test Case 2: Fail Scenario
Input: Birth Date : 15th Aug, 2022
Expected output: throw InvalidAgeException with message “Invalid Birth Date”
assertEquals
is static method of Assertions class to check that two objects are equal, if the condition provided to assertEquals
gets fail then our test case will get fail. assertThrows
asserts that the supplied executable throws the exception of the expected type.
If you don’t have assertions in the test then test case always passes. We will take a look about Assertion class later.
The lifecycle of a JUnit Test
We can have a lot of test cases in each JUnit test class and each test case goes through a lifecycle as below. We have an option to set up before all the tests run and before each test. We have the option of cleaning up after all the tests and after each test.
lifecycle per-method (Default Lifecycle): For every test method with in a test class one test instance will be created.
lifecycle per-class : For every test class one test instance will be created, not per test method.
@BeforeEach
→ denotes that the annotated method will be executed before each test method (JUnit4's@Before
)@AfterEach
→ denotes that the annotated method will be executed after each test method (JUnit4's@After
)@BeforeAll
→ denotes that the annotated method will be executed before all test methods in the current class (JUnit4's@BeforeClass
)@AfterAll
→ denotes that the annotated method will be executed after all test methods in the current class (JUnit4's@AfterClass
)
Let’s see the lifecycle with an implementation.
Output
Global count got incremented by once because init()
method executed only once as we have annotated it with @BeforeAll.
Local Count got increment by twice because beforeEachTest()
method executed by twice as we have annotated it with @BeforeEach.
JUnit 5 Assertions
JUnit 5 assertions help in validating the expected output with actual output of a testcase. To keep things simple, all JUnit Jupiter assertions are static methods in the org.junit.jupiter.Assertions
class as follow:
To Assert the Boolean variables.
To verify that two objects are equal.
To test an object’s null condition
To check the equality of two arrays.
To verifies that two Iterables are equal with a deep comparison
To test that variables do refer to the same object or not
To verifies all the multiple executables at once
Exception Handling : To expect that executable will throws the exception of the expected type
Custom Assert : If we want to fail a test on some condition then we can use
fail
method.
Useful JUnit’s Annotation
@DisplayName
Test classes and test methods can declare custom display names that will be displayed by test runners and test reports.
@Disabled
The @Disabled
annotation is used to disable or skip tests at class or method level. This is analogous to JUnit 4’s @Ignore
.
When declared at class level, all @test
methods are skipped. When we use @Disabled
at the method level, only the annotated method is disabled.
@Disabled
is used to signal that the annotated test method is currently disabled and should not be executed.
Dependency injection in JUnit 5
In all prior JUnit versions, test constructors or methods weren’t allowed to have parameters. As one of the major changes in JUnit 5, both test constructors and methods are now permitted to have parameters.
This allows for greater flexibility and enables dependency injection for constructors and methods.
TestInfo is used to inject information about the current test or container into test cases
Last But Not Least : Clean Test F.I.R.S.T Rule
F.I.R.S.T. clean tests follow five other rules that form the above acronym:
Fast : Tests should be fast. They should run quickly. When tests run slow, you won’t want to run them frequently.
Independent : Tests should not depend on each other. One test should not set up the conditions for the next test.
Repeatable : Tests should be repeatable in any environment. You should be able to run the tests in the DEV , QA, PROD environment without a network.
Self-Validating : The tests should have a boolean output. Either they pass or fail. You should not have to read through a log file to tell whether the tests pass.
Timely : The tests need to be written in a timely fashion. Unit tests should be written just before the production code that makes them pass.
Thank you !!!
“An investment in knowledge always pays the best interest.”
I hope this article was a good learning experience for you.
Thank you for following through with it to the end.
Be Tuned for Part 2, Mockito Framework
Happy Learning with Coding!..