For a web application developer, testing web applications is always a challenging task because getting a real-time test environment for web applications requires a lot of effort. But thanks to the Spring MVC Test framework, it simplifies the testing of Spring MVC applications.
But why do we need to consider putting effort into testing our application? Writing good test cases for our application is a kind of like buying an insurance policy for your application, although it does not add any functional values to your application, it will definitely save you time and effort by detecting functionality failures early. Consider your application growing bigger and bigger in terms of functionality—you need some mechanism to ensure that the existing functionalities were not disturbed by means of introducing new functionalities.
Testing frameworks provide you with such a mechanism, to ensure that your application's behavior is not altered due to refactoring or the addition of new code. They also ensure existing functionalities work as expected.
In this chapter, we are going to look at the following topics:
In software development, unit testing is a software testing method in which the smallest testable parts of source code, called units, are individually and independently tested to determine whether they behave exactly as we expect. To unit test our source code, all we need is a test program that can run a bit of our source code (unit), provide some inputs to each unit, and check the results for the expected output. Most unit tests are written using some sort of test framework set of library code designed to make writing and running tests easier. One such framework is called JUnit. It is a unit testing framework for the Java programming language.
Let's see how to test one of our domain objects using the JUnit framework to ensure it functions as expected. In an earlier chapter, we created a domain object to represent an item in a shopping cart called CartItem
. The CartItem
class has a method called getTotalPrice
to return the total price of that particular cart item, based on the product and number of items it represents. Let's test whether the getTotalPrice
method behaves properly:
pom.xml
, which you can find under the root directory of the project itself.pom.xml
file. Select the Dependencies tab and click on the Add button of the Dependencies section.junit
as Group Id, junit
as Artifact Id, 4.12
as Version, select Scope as test, click on the OK button, and save pom.xml
.CartItemTest
under the com.packt.webstore.domain
package in the src/test/java
source folder, add the following code to it, and save the file:package com.packt.webstore.domain; import java.math.BigDecimal; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class CartItemTest { private CartItem cartItem; @Before public void setup() { cartItem = new CartItem("1"); } @Test public void cartItem_total_price_should_be_equal_ to_product_unit_price_in_case_of_single_quantity() { //Arrange Product iphone = new Product("P1234","iPhone 5s", new BigDecimal(500)); cartItem.setProduct(iphone); //Act BigDecimal totalPrice = cartItem.getTotalPrice(); //Assert Assert.assertEquals(iphone.getUnitPrice(), totalPrice); } }
CartItemTest.java
and choose Run As | JUnit Test. You will see a failing test case in the JUnit window, as shown in the following screenshot:1
to the quantity
field of CartItem
in the setProduct
method of the CartItem
class as follows, and save the file: public void setProduct(Product product) {
this.product = product;
this.quantity = 1;
this.updateTotalPrice();
}
CartItemTest.java
again and choose Run As | JUnit Test. You will see a passing test case in the JUnit window, as shown in the following screenshot:As I already mentioned, the getTotalPrice
method of the CartItem
class is designed to return the correct total price based on the product and the number of products it represents. But to ensure its behavior, we wrote a test program called CartItemTest
under the com.packt.webstore.domain
package in the src/test/java
source folder, as mentioned in step 4.
In CartItemTest
, we used some of the JUnit framework APIs such as the @Test
and @Before
annotations, and more. So in order to use these annotations in our CartItemTest
class, prior to that we need to add the JUnit
JAR as a dependency in our project. That's what we did from steps 1 to 3.
Now you need to understand the CartItemTest
class thoroughly. The important method in the CartItemTest
class is the @Test
annotated method called cartItem_total_price_should_be_equal_to_product_unit_price_in_case_of_single_quantity
. The @Test
(org.junit.Test
) annotation marks a particular method as a test method so that the JUnit framework can treat that method as a test method and execute it when we choose the Run As | JUnit Test option:
@Test public void cartItem_total_price_should_be_equal_to_product_unit_price_in_case_of_single_quantity() { //Arrange Product iphone = new Product("P1234","iPhone 5s", new BigDecimal(500)); cartItem.setProduct(iphone); //Act BigDecimal totalPrice = cartItem.getTotalPrice(); //Assert Assert.assertEquals(iphone.getUnitPrice(), totalPrice); }
If you notice, this method was divided into three logical parts called Arrange, Act, and Assert:
In the Arrange part, we just instantiated a product domain object (iphone
) with a unit price value of 500
and added that product object to the cartItem
object by calling cartItem.setProduct(iphone)
. Now we have added a single product to the cartItem
, but we haven't altered the quantity
of the cartItem
object. So if we call the getTotalPrice
method of cartItem
, we must get 500
(in BigDecimal
), because the unit price of the domain object (iphone
) we added in cartItem
is 500
.
In the Act part, we just called the method under test, which is the getTotalPrice
method of the cartItem
object, and stored the result in a BigDecimal
variable called totalPrice
. Later, in the Assert part, we used the JUnit API (Assert.assertEquals
) to assert the equality between the unitPrice
of the product domain object and the calculated totalPrice
of cartItem
:
Assert.assertEquals(iphone.getUnitPrice(), totalPrice);
The totalPrice
of cartItem
must be equal to the unitPrice
of the product domain object, which we added to cartItem
, because we added a single product domain object whose unitPrice
needs to be the same as the totalPrice
of cartItem
.
When we run our CartItemTest
as mentioned in step 5, the JUnit framework tries to execute all the @Test
annotated methods in the CartItemTest
class. So based on the Assertion result, a test case may fail or pass. In our case, our test case failed. You can see the failure trace showing an error message saying expected <500> but was: <0> in the screenshot for step 5. This is because in the Arrange part, when we added a product domain object to cartItem
, it doesn't update the quantity
field of the cartItem
object. It is a bug, so to fix this bug we default the quantity
field value to 1 whenever we set the product
argument using the setCartItem
method, as mentioned in step 6. Now finally, if we run our test case again, this time it passes as expected.
It's good that we tested and verified the getTotalPrice
method of the CartItem
class, but can you similarly write a test class for the Cart
domain object class? In the Cart
domain object class, there is method to get the grand total (getGrandTotal
), and write various test case to check whether the getGrandTotal
method works as expected.