Agile software testing

The term Agile, in the world of software development, typically refers to an approach to project management that aims to unite teams around the principles of collaboration, simplicity, flexibility, and responsiveness throughout the process of developing a new program in an application.

An Agile software testing means the practice of testing software for any performance issues or bugs within the context of Agile workflow. The developers and testers, in the agile approach, are seen as the two sides of the same coin. The Agile software testing includes unit testing and integration testing. It helps with executing the tests as quickly as possible.

Let's understand the significance and the objectives of unit and integration testing.

Unit testing

Unit testing, as the name suggests, is the testing of every individual method of the code. It is the method of testing the fundamental pieces of your functionality. It is a piece of code written by the software developer to test a specific functionality of the code. Unit tests are used for improving the quality of the code and preventing bugs. They are not commonly used for finding them. They are automated testing frameworks.

Let's take an example of the EmployeeService class that needs the employeeDao object for loading the data from the database. This employeeDao is a real object. So, to test the EmployeeService class, it is required to provide the employeeDao object that has a valid connection to the database. We also have to insert the data needed for the test into the database.

Inserting the data into the database after setting up the connection and then testing on an actual database can be a lot of work. Instead, we can provide the EmployeeService instance with a fake EmployeeDao class, which will just return the data that we need to complete the test. This fake EmployeeDao class will not read any data from the database. This fake EmployeeDao class is a mock object that is a replacement for a real object, which makes it easier to test the EmployeeService class.

A common technique that can be applied while testing a unit that depends on other units is to simulate the unit's dependencies with stubs and mock objects, which help in reducing the complexity because of the dependencies in the unit test. Let's look at each of them in detail:

  • Stub: A stub is a dummy object, which simulates real objects with the minimum number of methods required for a unit test. It can be configured to the return value by implementing the methods in a predetermined way along with the hardcoded data that suite the test.
  • Mock: A mock object is a fake object or a substitute object that is added to the system, and it usually knows how its method is expected to be called for a test, and decides whether the unit test has passed or failed. The mock object tests whether the real object interacted as expected with the fake object. There may be one or more mock objects per test. A mock object is an object which mimics an actual object. In Java, there are several libraries, which are available for implementing mocking, including jMock, EasyMock, and Mockito (we are interested in this particular tool).

State verification is used to check whether the actual method returns the correct value. Behavior verification is used to check whether the correct method was called. Stub is used for state verification, whereas a mock object is used for behavior verification. A stub object cannot fail a unit test but a mock object can. This is because we know what and why we are implementing a stub object, whereas a mock is just a fake object that mimics a real object and if the business logic in the code is wrong, then the unit test fails even if we have passed a real object.

Unit testing for isolated classes

Unit testing is easy for the isolated class, which tests either the class or its method in isolation. Let's create unit tests for the isolated class, where the class under testing will not directly depend on any other class, as shown in the following diagram:

Unit testing for isolated classes

The core functions of the HrPayroll system should be designed around employee details. First, we need to create the Employee class and override the equals() method, as shown in the following code snippet:

package org.packt.Spring.chapter9.SpringTesting.modle;

public class Employee {

   private String employeeId;
   private String firstName;
   private String lastName;
   private int salary;

   // constructor, Getters and setters

   @Override
   public boolean equals(Object obj) {

         if (!(obj instanceof Employee)) {
                return false;
         }

         Employee employee = (Employee) obj;
         return employee.employeeId.equals(employeeId);
   }
}

Now, to persist the employee object to the HrPayroll system, we need to define the EmployeeDao interface:

package org.packt.Spring.chapter9.SpringTesting.dao;

import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

public interface EmployeeDao {

   public void createEmployee(Employee employee);

   public void updateEmployee(Employee employee);

   public void deleteEmployee(String employeeId);

   public Employee findEmployee(String employeeId);

}

Let's implement the EmployeeDao interface to demonstrate the unit testing for this isolated class:

package org.packt.Spring.chapter9.SpringTesting.dao;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

public class InMemeoryEmployeeDaoImpl implements EmployeeDao {

   private Map<String, Employee> employees;

   public InMemeoryEmployeeDaoImpl() {
         employees = Collections
                     .synchronizedMap(new HashMap<String, Employee>());
   }

   public boolean isOldEmployee(String employeeId) {
         return employees.containsKey(employeeId);
   }

   @Override
   public void createEmployee(Employee employee) {
          if (!isOldEmployee(employee.getEmployeeId())) {
                employees.put(employee.getEmployeeId(), employee);
          }
   }

   @Override
   public void updateEmployee(Employee employee) {
          if (isOldEmployee(employee.getEmployeeId())) {
                employees.put(employee.getEmployeeId(), employee);
           }
   }

   @Override
   public void deleteEmployee(String employeeId) {
         if (isOldEmployee(employeeId)) {
                employees.remove(employeeId);
         }
   }

   @Override
   public Employee findEmployee(String employeeId) {
         return employees.get(employeeId);
   }
}

From the aforementioned code snippet, we can see that the InMemeoryEmployeeDaoImpl class doesn't depend on any other class directly, which makes it easier to test, because we don't need to be worried about setting dependency and their working.

Here is an implementation of InMemeoryEmployeeDaoTest:

package org.packt.Spring.chapter9.SpringTesting.test;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.packt.Spring.chapter9.SpringTesting.dao.InMemeoryEmployeeDaoImpl;
import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

public class InMemeoryEmployeeDaoTest {

   private static final String OLD_EMPLOYEE_ID = "12121";
   private static final String NEW_EMPLOYEE_ID = "53535";

   private Employee oldEmployee;
   private Employee newEmployee;
   private InMemeoryEmployeeDaoImpl empDao;

The setUp() method is annotated with the @Before annotation, as shown in the code snippet here:

   @Before
   public void setUp() {
          oldEmployee = new Employee(OLD_EMPLOYEE_ID, "Ravi", "Soni", 1001);
          newEmployee = new Employee(NEW_EMPLOYEE_ID, "Shashi", "Soni", 3001);

          empDao = new InMemeoryEmployeeDaoImpl();
          empDao.createEmployee(oldEmployee);
   }

The isOldEmployeeTest() method is annotated by the @Test annotation. This test method verifies the employeeId, as shown in the following code snippet:

   @Test
   public void isOldEmployeeTest() {

   Assert.assertTrue(empDao.isOldEmployee(OLD_EMPLOYEE_ID));

   Assert.assertFalse(empDao.isOldEmployee(NEW_EMPLOYEE_ID));
   }

The createNewEmployeeTest() method is annotated by the @Test annotation. This test method creates a new employee and then verifies the new employeeId:

   @Test
   public void createNewEmployeeTest() {
          empDao.createEmployee(newEmployee);

   Assert.assertTrue(empDao.isOldEmployee(NEW_EMPLOYEE_ID));
   }

The updateEmployeeTest() method is annotated by the @Test annotation. This test method updates employee details and then verifies the employee's firstName, as shown here:

   @Test
   public void updateEmployeeTest() {
          String firstName = "Sharee";
          oldEmployee.setFirstName(firstName);
          empDao.updateEmployee(oldEmployee);
          Assert.assertEquals(firstName, empDao.findEmployee(OLD_EMPLOYEE_ID)
                      .getFirstName());
   }

The deleteEmployeeTest() method is annotated by the @Test annotation. This test method deletes employee details and then verifies the employee ID, as shown in the following code snippet:

   @Test
   public void deleteEmployeeTest() {
          empDao.deleteEmployee(OLD_EMPLOYEE_ID);
   Assert.assertFalse(empDao.isOldEmployee(OLD_EMPLOYEE_ID));
   }
}

The test results of the aforementioned test cases will be as shown here:

Unit testing for isolated classes

Unit testing for dependent class using mock objects

As we have seen in the previous section, testing either an isolated class or an independent class is easy. However, it would be a little more difficult to test a class that depends on another class, such as the EmployeeService class (that holds business logic), which depends on the EmployeeDao class (this class knows how to communicate with the database and get the information). Unit testing is harder and has dependencies, as shown here:

Unit testing for dependent class using mock objects

Class Under Test means that whenever we write a unit test, generally the term "unit" refers to a single class against which we have written the tests. It is the class that is being tested. So it's good to remove the dependencies, create a mock object and continue with the unit testing, as shown in the following diagram:

Unit testing for dependent class using mock objects

The concept behind removing the dependencies and creating a mock object is that by creating an object that can take the place of a real dependent object. If we are writing a unit test for our EmployeeService around business logic, then that particular unit test should not connect EmployeeService to the EmployeeDao intern, and then connect the EmployeeDao intern to the database and perform a crud operation, because we just want to perform the testing of the EmployeeService class, and so we need to create a mock EmployeeDao. The Mockito framework allows us to create the mock object.

The Mockito framework

The Mockito framework is an open source mock framework for unit testing; it was originally based on EasyMock, which can be downloaded from either http://mockito.org/ or https://code.google.com/p/mockito/. It can be used in conjunction with other testing tools, such as JUnit. It helps in creating and configuring mock objects. Add the Mockito JAR to your CLASSPATH along with JUnit. It uses the field-level annotations, as shown here:

  • @Mock: This creates the mock object for an annotated field.
  • @Spy: This creates spies for the objects or the files it annotates.
  • @InjectMocks: The private field that is annotated by the @InjectMocks annotations is instantiated and Mockito injects the fields annotated with either the @Mock annotation or the @Spy annotation to it.
  • @RunWith(MockitoJUnitRunner.class): If you use the aforementioned annotations, then it is must be done to annotate the test class with this annotation to use the MockitoJUnitRunner. When MockitoJUnitRunner executes the unit tests, it creates mock objects and spy objects for all the fields annotated by the @Mock annotation or the @Spy annotation.

Let's perform the unit testing using Mockito, where we create a mock object for a dependent object. Here is the code for the EmployeeService.java interface:

package org.packt.Spring.chapter9.SpringTesting.service;

import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

public interface EmployeeService {

   public Employee findEmployee(String employeeId);

}

The following is an implementation of EmployeeService:

package org.packt.Spring.chapter9.SpringTesting.service;

import org.packt.Spring.chapter9.SpringTesting.dao.EmployeeDao;
import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

public class EmployeeServiceImpl implements EmployeeService {

   private EmployeeDao employeeDao = null;

   public EmployeeServiceImpl(EmployeeDao employeeDao) {
          this.employeeDao = employeeDao;
   }

   @Override
   public Employee findEmployee(String employeeId) {
         return employeeDao.findEmployee(employeeId);
   }
}

And, here we have created our test class in the test folder, and created a mock object by annotating EmployeeDao. We have annotated the class by the @RunWith(MockitoJUnitRunner.class) annotation. We have created two test methods by using the @Test annotation, where, in the first test case, we verify that the findEmployee behavior happened once and in the second test case, we verify that no interactions happened on employeeDao mocks:

package org.packt.Spring.chapter9.SpringTesting.service;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.packt.Spring.chapter9.SpringTesting.dao.EmployeeDao;
import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

@RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceTest {

   private static final String OLD_EMPLOYEE_ID = "12121";
   private Employee oldEmployee;
   private EmployeeService employeeService;

   @Mock
   private EmployeeDao employeeDao;

   @Before
   public void setUp() {
         employeeService = new EmployeeServiceImpl(employeeDao);
         oldEmployee = new Employee(OLD_EMPLOYEE_ID, "Ravi", "Soni", 1001);
   }

   @Test
   public void findEmployeeTest() {

   when(employeeDao.findEmployee(OLD_EMPLOYEE_ID)).thenReturn(oldEmployee);
          Employee employee = employeeService.findEmployee(OLD_EMPLOYEE_ID);
          Assert.assertEquals(oldEmployee, employee);

          // Verifies findEmployee behavior happened once
          verify(employeeDao).findEmployee(OLD_EMPLOYEE_ID);

          // asserts that during the test, there are no other calls to the mock
         // object.
         verifyNoMoreInteractions(employeeDao);
   }

   @Test
   public void notFindEmployeeTest() {

   when(employeeDao.findEmployee(OLD_EMPLOYEE_ID)).thenReturn(null);
           Employee employee = employeeService.findEmployee(OLD_EMPLOYEE_ID);
          Assert.assertNotSame(oldEmployee, employee);

          verify(employeeDao).findEmployee(OLD_EMPLOYEE_ID);

          // Verifies that no interactions happened on employeeDao mocks
          verifyZeroInteractions(employeeDao);
          verifyNoMoreInteractions(employeeDao);
   }
}

And, the result of running the test as JUnit is as follows:

The Mockito framework

Integration testing

Integration testing is a phase of software testing in which individual software modules are combined and tested as a group to ensure that the required units are properly integrated and interact correctly with each other. The purpose of integration testing is to verify the functionality, performance, and reliability of the code. Integration testing is used for testing several units together.

Let's take an example. We can create an integration test to test EmployeeServiceImpl using InMemeoryEmployeeDaoImpl as a DAO implementation:

package org.packt.Spring.chapter9.SpringTesting.service;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.packt.Spring.chapter9.SpringTesting.dao.EmployeeDao;
import org.packt.Spring.chapter9.SpringTesting.dao.InMemeoryEmployeeDaoImpl;
import org.packt.Spring.chapter9.SpringTesting.modle.Employee;

public class EmployeeServiceIntegrationTest {

   private static final String OLD_EMPLOYEE_ID = "12121";
   private static final String NEW_EMPLOYEE_ID = "53535";

   private Employee oldEmployee;
   private Employee newEmployee;
   private EmployeeService employeeService;

   @Before
   public void setUp() {
          oldEmployee = new Employee(OLD_EMPLOYEE_ID, "Ravi", "Soni", 1001);
          newEmployee = new Employee(NEW_EMPLOYEE_ID, "Shashi", "Soni", 3001);

          employeeService = new EmployeeServiceImpl(
                      new InMemeoryEmployeeDaoImpl());
          employeeService.createEmployee(oldEmployee);
   }

   @Test
   public void isOldEmployeeTest() {

   Assert.assertTrue(employeeService.isOldEmployee(OLD_EMPLOYEE_ID));

   Assert.assertFalse(employeeService.isOldEmployee(NEW_EMPLOYEE_ID));
   }

   @Test
   public void createNewEmployeeTest() {
         employeeService.createEmployee(newEmployee);

   Assert.assertTrue(employeeService.isOldEmployee(NEW_EMPLOYEE_ID));
   }

   @Test
   public void updateEmployeeTest() {
          String firstName = "Sharee";
          oldEmployee.setFirstName(firstName);
          employeeService.updateEmployee(oldEmployee);
          Assert.assertEquals(firstName,

   employeeService.findEmployee(OLD_EMPLOYEE_ID).getFirstName());
   }

   @Test
   public void deleteEmployeeTest() {
          employeeService.deleteEmployee(OLD_EMPLOYEE_ID);

   Assert.assertFalse(employeeService.isOldEmployee(OLD_EMPLOYEE_ID));
   }

}

The result is shown here:

Integration testing
..................Content has been hidden....................

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