Unit Testing
Let's take a closer look at the concept of unit testing and why it's necessary.
JUnit Basics
JUnit is a framework for unit testing that provides annotations for defining test methods and assertions to check expected results.
You've already encountered annotations when overriding different methods. In simple terms, annotations tell the compiler or framework what to do with a particular module. They also provide information to the programmer about the meaning of a specific line or module in the program. Annotations greatly simplify life and are used in almost every framework. Let's take a look at a simple example of a unit test, and you'll understand how annotations are used:
In this example:
- The
@Test
annotation indicates that the methodtestAddition()
is a test method; Calculator
is the class we are testing;Assertions.assertEquals(8, result)
checks that the addition result equals 8.
Therefore, we have verified that the add()
method correctly performs its function. However, a single unit test is not sufficient, as there could be many other issues. For example, a user might pass null
instead of a number, or the numbers could fall outside the acceptable range. Such cases should also be covered by unit tests.
Note
We will delve into writing and learning unit tests in the next section. This chapter is designed for informational purposes and to help you understand why writing unit tests is necessary.
What should unit tests check?
Let's imagine that we need to test a simple method. For this purpose, we will write a method that takes a string in the form of "Name||Address||email||phoneNumber||"
and splits it into a map with corresponding keys and values.
This method code will look something like this:
Main.java
Code Description
Main
class that includes a method convertToMap
for converting a structured string into a map:HashMap
and Map
from the java.util
package;Main
class is defined as public;convertToMap
:"||"
as the delimiter, and stores these parts in a Map
with predefined keys ("Name", "Address", "Email", "PhoneNumber");convertToMap
to convert it to a map, and prints the map to the console;The code essentially parses a delimited string into a map structure, making it easy to access individual data components using their corresponding keys.
Note
To split this format, we need to split by the
||
symbol, but we must indicate to the compiler that it is indeed a symbol and not a command. Therefore, we use\\
before the symbol. Thus, our regex looks like this:\\|\\|
.
The method is quite simple to write. But let's now return to what exactly test cases exist for this method:
- Test of Correct Data
Testing with correctly formatted input string. Expecting the method to return a
Map
with the correct values for each key; - Test of Incorrect Format
Testing with a string that does not match the expected format (e.g., missing some of the required
||
separators or having them in excess); - Test of Empty String
Checking the method's behavior when the input string is empty. The method should either return an empty
Map
or throw an exception; - Test of Null String
Testing the method's behavior when
null
is passed as the input string.
Let's go step by step. In the first test case, we will test the functioning of this method with correct data. In the main
method, we have already done this when we passed a value and received the correct data.
In the second test case, we need to pass an incorrect format to see how the method will behave. For example, let's pass data separated by a single |
character:
main.java
As you can see, we are throwing an IndexOutOfBoundsException
. This means that the array we create from the original string does not have 4 elements as it should have. The test showed us that our method is not perfect, so let's add exception handling using a simple if statement:
Here, we've added a basic check that, in case incorrect data is passed, will return an empty map and print a message about the method's incorrect operation.
Let's look at a runnable example:
main.java
As you can see, the third test case is also covered by this check; If an empty string is passed, it won't pass the validation we wrote.
Let's move on to the case where a null
value is passed. We don't currently check the string for null, so we need to add this check in the method:
We have added a null
check, and now the method will return an empty map if null
is passed to it.
Let's run this code and see the result:
main.java
So, we have a refined method that handles various errors and different parameter-passing cases. We tested this method manually without using the JUnit library. However, in the next section, we will do this much faster using this library and unit tests.
¿Todo estuvo claro?
Contenido del Curso
Java JUnit Library. Types of Testing
Java JUnit Library. Types of Testing
Unit Testing
Let's take a closer look at the concept of unit testing and why it's necessary.
JUnit Basics
JUnit is a framework for unit testing that provides annotations for defining test methods and assertions to check expected results.
You've already encountered annotations when overriding different methods. In simple terms, annotations tell the compiler or framework what to do with a particular module. They also provide information to the programmer about the meaning of a specific line or module in the program. Annotations greatly simplify life and are used in almost every framework. Let's take a look at a simple example of a unit test, and you'll understand how annotations are used:
In this example:
- The
@Test
annotation indicates that the methodtestAddition()
is a test method; Calculator
is the class we are testing;Assertions.assertEquals(8, result)
checks that the addition result equals 8.
Therefore, we have verified that the add()
method correctly performs its function. However, a single unit test is not sufficient, as there could be many other issues. For example, a user might pass null
instead of a number, or the numbers could fall outside the acceptable range. Such cases should also be covered by unit tests.
Note
We will delve into writing and learning unit tests in the next section. This chapter is designed for informational purposes and to help you understand why writing unit tests is necessary.
What should unit tests check?
Let's imagine that we need to test a simple method. For this purpose, we will write a method that takes a string in the form of "Name||Address||email||phoneNumber||"
and splits it into a map with corresponding keys and values.
This method code will look something like this:
Main.java
Code Description
Main
class that includes a method convertToMap
for converting a structured string into a map:HashMap
and Map
from the java.util
package;Main
class is defined as public;convertToMap
:"||"
as the delimiter, and stores these parts in a Map
with predefined keys ("Name", "Address", "Email", "PhoneNumber");convertToMap
to convert it to a map, and prints the map to the console;The code essentially parses a delimited string into a map structure, making it easy to access individual data components using their corresponding keys.
Note
To split this format, we need to split by the
||
symbol, but we must indicate to the compiler that it is indeed a symbol and not a command. Therefore, we use\\
before the symbol. Thus, our regex looks like this:\\|\\|
.
The method is quite simple to write. But let's now return to what exactly test cases exist for this method:
- Test of Correct Data
Testing with correctly formatted input string. Expecting the method to return a
Map
with the correct values for each key; - Test of Incorrect Format
Testing with a string that does not match the expected format (e.g., missing some of the required
||
separators or having them in excess); - Test of Empty String
Checking the method's behavior when the input string is empty. The method should either return an empty
Map
or throw an exception; - Test of Null String
Testing the method's behavior when
null
is passed as the input string.
Let's go step by step. In the first test case, we will test the functioning of this method with correct data. In the main
method, we have already done this when we passed a value and received the correct data.
In the second test case, we need to pass an incorrect format to see how the method will behave. For example, let's pass data separated by a single |
character:
main.java
As you can see, we are throwing an IndexOutOfBoundsException
. This means that the array we create from the original string does not have 4 elements as it should have. The test showed us that our method is not perfect, so let's add exception handling using a simple if statement:
Here, we've added a basic check that, in case incorrect data is passed, will return an empty map and print a message about the method's incorrect operation.
Let's look at a runnable example:
main.java
As you can see, the third test case is also covered by this check; If an empty string is passed, it won't pass the validation we wrote.
Let's move on to the case where a null
value is passed. We don't currently check the string for null, so we need to add this check in the method:
We have added a null
check, and now the method will return an empty map if null
is passed to it.
Let's run this code and see the result:
main.java
So, we have a refined method that handles various errors and different parameter-passing cases. We tested this method manually without using the JUnit library. However, in the next section, we will do this much faster using this library and unit tests.
¿Todo estuvo claro?