Chapter 12. Testing Your Application

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:

  • Testing the domain object and validator
  • Testing Controllers
  • Testing RESTful web services

Unit testing

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.

Time for action - unit testing domain objects

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:

  1. Open pom.xml, which you can find under the root directory of the project itself.
  2. You will be able to see some tabs at the bottom of the pom.xml file. Select the Dependencies tab and click on the Add button of the Dependencies section.
  3. A Select Dependency window will appear; enter 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.
  4. Now create a class called 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); 
              } 
          } 
    
  5. Now right-click on 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:
    Time for action - unit testing domain objects

    JUnit failing test case in CartItemTest

  6. To make the test case pass, assign the value 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(); 
          } 
    
  7. Now right-click on 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:
    Time for action - unit testing domain objects

    JUnit passing test case in CartItemTest

What just happened?

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:

  • Arrange all the necessary preconditions and inputs to perform a test
  • Act on the object or method under test
  • Assert that the expected results have occurred

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.

Have a go hero - adding tests for Cart

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.124.143