Introduction
“Nobody likes testing. Neither I”. Yes, you heard it right. I said I don’t like testing. Well, that’s not only me, it’s true for MOST of the developers around the world. But actually, testing is an essential phase of any software development life-cycle. And that is what we are going to learn in this tutorial series with JUnit 5 – a software testing framework in Java.
What is Software Testing?
We as a programmer write code to solve various kinds of problems. Whenever we complete our coding the very immediate step is to verify that it works as expected. This verification is called testing.
Software testing is a process of evaluating the software functionality and verification that it meets the Software Requirement Specification. It helps in finding defects in application code and ensures the final product is defect-free. And thus, improves the quality of the software product.
What are the test cases?
Test cases are preconditions with given data, values, and expected results. These preconditions could be either for a specific piece of the software or for the whole system itself. Test cases help determine whether the software does what it is supposed to do or not. It also helps to verify the software or the solution under test works well for edge cases too.
Let’s say you have a Calculator
class like below:
class Calculator { public int add(int number1, number2){ return number1+number2; } }
The class is pretty simple. It has just one method add
which accepts two integers and returns their sum. Now, we need to test the correct implementation of this add functionality.
In general, if you want to test the function, you can do something like below:
class CalculatorTest{ public static void main(String args[]){ Calculator calc = new Calculator(); int sum = calc.add(1, 2); if(sum == 3){ System.out.println("Test Passed"); } else { System.out.println("Test Failed"); } } }
Here, we are creating the instance of Calculator
class and calling it’s add
method with parameters as 1
and 2
.
We now expect the return value should be 3
. If actual value (return value of method call) matches the expected value then we say test is passed otherwise failed.
To run this test case, we need to run CalculatorTest
manually every time. We can avoid this using a testing framework.
Why write test cases?
If it’s just a matter of verification of code, one can do it by doing manual tests then why to write test cases for it? Well, the test cases are more than just verification.
Test cases provide a safety net to prevent your code from breaking in the future.
When somebody, probably in your team, modifies or adds new functionality, your code should not break. But this is not feasible to verify in manual testing.
Manual testing can’t be a long term way, especially when code-base starts increasing gradually.
Test cases are not only for present, they are also for future.
Why need a testing framework?
“If I can write a few more codes to test my code then why do I need a testing framework?” – Before you ask this, let me ask you a few questions.
- Of course, you can write additional code to test your code but then how will you keep track of what passes and what does not?
- If you think you can use console or print statements then how much effort will it need you to figure out the problems?
- And what if you have some specific requirements such as “Perform Task A if the test fails” or “Perform Task B if the test passes”?
A good testing framework is an answer to all the above questions. I hope it’s clear now that a testing framework gives a lot of additional benefits.
What is JUnit 5?
JUnit is a popular unit Testing Framework in Java. You may consider it as the de-facto standard for unit testing in the Java ecosystem. It is written in Java. At the time of writing this article, JUnit 5 is the latest stable version of the framework.
The architecture of JUnit 5
Below is a high level architectural overview of JUnit 5.
To understand, it can be divided into 3 modules. The first and important one is the JUnit Platform itself. The platform is the core of the framework. IDEs or build tools can be a few good examples that access the core platform directly to achieve customized behavior.
The second is Jupiter. Jupiter is a set of new APIs written on top of the JUnit platform. It provides many rich features to the developers for writing effective test cases with the least configuration. We’ll be working with this module most throughout the tutorial.
Last but not the least, Vintage. As the name depicts that it is older JUnit. This module is there to provide backward compatibility. Almost all old JUnit test cases could be still run on JUnit 5 with the help of this module.
And, if you want to write your own set of APIs or customization, you can also use platform directly and create your own library/API. Many third-party libraries are there that uses platform directly.
Test case example using JUnit
Let’s re-write the test case using JUnit for the same above Calculator
class’s add
method.
class CalculatorTest{ @Test public void testAdd() { Calculator calc = new Calculator(); int expectation = 3; int actual = calc.add(1, 2); Assertions.assertEquals(expectation, actual); } }
Running this test case gives the below output:
The test case is passed. That means expectation meets reality in code as well 😜. If we change the expectation
to 4 the test would fail as below.
You can see here, the IDE is indicating that how many and what test cases are passed or failed. It also tells the difference between expected
and actual
value. Not only IDEs but many build tools and other CI/CD tools uses JUnit (the framework) to give you more control with extra flexibility than manual testing.
Conclusion
To summarise, we learned the basic theory about testing. We also learned why testing and testing framework is really important. Then we looked at JUnit 5 high-level architecture and its working. I would love to hear your thoughts on this, feel free to drop in comments below.