Book Image

Mockito Essentials

By : Sujoy Acharya
Book Image

Mockito Essentials

By: Sujoy Acharya

Overview of this book

Table of Contents (14 chapters)

Working with stubs


A stub delivers indirect inputs to the caller when the stub's methods are called. Stubs are programmed only for the test scope. Stubs may record other information such as how many times they are invoked and so on.

Unit testing a happy path is relatively easier than testing an alternate path. For instance, suppose that you need to simulate a hardware failure or transaction timeout scenario in your unit test, or you need to replicate a concurrent money withdrawal for a joint account use case—these scenarios are not easy to imitate. Stubs help us to simulate these conditions. Stubs can also be programmed to return a hardcoded result; for example, a stubbed bank account object can return the account balance as $100.00.

The following steps demonstrate stubbing:

  1. Launch Eclipse, open <work_space>, and go to the 3605OS_TestDoubles project.

  2. Create a com.packt.testdoubles.stub package and add a CreateStudentResponse class. This Plain Old Java Object (POJO) contains a Student object and an error message:

      public class CreateStudentResponse {
        private final String errorMessage;
        private final Student student;
    
        public CreateStudentResponse(String errorMessage, Student student) {
          this.errorMessage = errorMessage;
          this.student = student;
        }
        public boolean isSuccess(){
          return null == errorMessage;
        }
    
        public String getErrorMessage() {
          return errorMessage;
        }
         public Student getStudent() {
         return student;
        }
      }
  3. Create a StudentDAO interface and add a create() method to persist a student's information. The create () method returns the roll number of the new student or throws an SQLException error. The following is the interface definition:

    public interface StudentDAO {
      public String create(String name, String className)
      throws SQLException;
    }
  4. Create an interface and implementation for the student's registration. The following service interface accepts a student's name and a class identifier and registers the student to a class. The create API returns a CreateStudentResponse. The response contains a Student object or an error message:

    public interface StudentService {
      CreateStudentResponse create(String name, String studentOfclass);
    }

    The following is the service implementation:

    public class StudentServiceImpl implements StudentService {
      private final StudentDAO studentDAO;
    
      public StudentServiceImpl(StudentDAO studentDAO) {
        this.studentDAO = studentDAO;
      }
    
      @Override public CreateStudentResponse create(String name, String studentOfclass) {
        CreateStudentResponse response = null;
        try{
          String roleNum= studentDAO.create (name, studentOfclass);
          response = new CreateStudentResponse(null, new Student(roleNum, name));
        }catch(SQLException e) {){
          response = new CreateStudentResponse
          ("SQLException"+e.getMessage(),  null);
        }catch (Exception e) {
          response = new CreateStudentResponse(e.getMessage(), null);
        }
        return response;
      }
    }

    Note

    Note that the service implementation class delegates the Student object's creation task to the StudentDAO object. If anything goes wrong in the data access layer, then the DAO throws an SQLException error. The implementation class catches the exceptions and sets the error message to the response object.

  5. How can you test the SQLException condition? Create a stub object and throw an exception. Whenever the create method is invoked on the stubbed DAO, the DAO throws an exception. The following ConnectionTimedOutStudentDAOStub class implements the StudentDAO interface and throws an SQLException error from the create() method:

    package com.packt.testdoubles.stub;
    import java.sql.SQLException;
    
    public class ConnectionTimedOutStudentDAOStub implements StudentDAO {
      public String create(String name, String className)
      throws SQLException {
        throw new SQLException("DB connection timed out");
      }
    }

    This class should be created under the test source folder since the class is only used in tests.

  6. Test the SQLException condition. Create a test class and pass the stubbed DAO to the service implementation. The following is the test code snippet:

    public class StudentServiceTest {
      private StudentService studentService;
      @Test
      public void when_connection_times_out_then_the_student_is_not_saved() {
        studentService = new StudentServiceImpl(new ConnectionTimedOutStudentDAOStub());
        String classNine = "IX";
        String johnSmith = "john Smith";
        CreateStudentResponse resp = studentService.create(johnSmith, classNine);
        assertFalse(resp.isSuccss());
      }
    }

    The error condition is stubbed and passed into the service implementation object. When the service implementation invokes the create() method on the stubbed DAO, it throws an SQLException error.

Stubs are very handy to impersonate error conditions and external dependencies (you can achieve the same thing with a mock; this is just one approach). Suppose you need to test a code that looks up a JNDI resource and asks the resource to return some value. You cannot look up a JNDI resource from a JUnit test; you can stub the JNDI lookup code and return a stubbed object that will give you a hardcoded value.