Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Mockito Essentials
Mockito Essentials

Mockito Essentials: A practical guide to get you up and running with unit testing using Mockito

eBook
€8.99 €19.99
Paperback
€24.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with a Packt Subscription?

Free for first 7 days. €18.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing
Table of content icon View table of contents Preview book icon Preview Book

Mockito Essentials

Chapter 1. Exploring Test Doubles

"I never make stupid mistakes. Only very, very clever ones."

–John Peel

It is very difficult to find stupid mistakes, but it's even more daunting when you are trying to figure out the clever ones. Debugging an application to know how to fix a problem is very expensive and time-consuming. Automated unit tests provide an extremely effective mechanism for catching regressions, especially when combined with test-driven development; it creates a test safety net for the developers.

This chapter covers the concepts of unit testing, quality of unit tests, external dependencies, and test doubles.

The Working with unit tests section introduces you to test automation and describes the characteristics of a good unit test.

The Understanding test doubles section explores the concept of external dependency and provides examples of test doubles. The following test doubles are explored:

  • Dummy objects
  • Stubs
  • Spies
  • Mock objects
  • Fake objects

Working with unit tests

A common understanding of unit testing is the testing of the smallest possible part of software, such as a single method, a small set of related methods, or a class.

In reality, we do not test methods; we test a logical unit and its behavior instead. Logical units can extend to a single method, to an entire class, or a collaboration of multiple classes.

For example, a standard calculator program can have an add method for adding two numbers. We can verify the add behavior by invoking the add method, or we can design the calculator program to have a simple calculate API, which can take two numbers and an operation (add, subtract, divide, and so on). Depending on the operand type (integer, double, and so on), the calculator may delegate the calculation to a collaborator class, such as a double calculator or a long calculator. We can still unit test the add behavior, but multiple classes (units) are involved now.

A unit test verifies an assumption about the behavior of the system. Unit tests should be automated to create a safety net so that the assumptions are verified continuously and a quick feedback can be provided if anything goes wrong.

The following are the benefits of test automation:

  • Behavior is continually verified: We refactor code (change the internal structure of the code without affecting the behavior of the system) to improve the code's quality, such as maintainability, readability, or extensibility. We can refactor code with confidence if automated unit tests are running and giving feedback.
  • The side effects of code changes are detected immediately: This is useful for a fragile, tightly-coupled system, where a change in one module breaks another module.
  • Saves time; no need for immediate regression testing: Suppose that you are adding a scientific computational behavior to an existing calculator program and modifying the code; after every piece of change, you do a regression testing to verify the integrity of the system. Manual regression testing is tedious and time-consuming, but if you have an automated unit test suite, then you can delay the regression testing until the functionality is done. This is because the automated suite will inform you at every stage if you break an existing feature.

A unit test should exhibit the following characteristics:

  • It should be automated, as explained in the preceding section.
  • It should have a fast test execution. To be precise, a test should not take more than a few milliseconds to finish execution (they should be fast; the faster, the better). A system can have thousands of unit tests. If they take time to execute, then the overall test execution time will go up; as a result, no one will be interested in running the tests. It will impact the feedback cycle.
  • A test should not depend on the result of another test or rather test execution order. Unit test frameworks can execute tests in any order. So, if a test depends on another test, then the test may fail any time and provide wrong feedback. You want tests to be standalone so that you can look at them and quickly see what they're actually testing, without having to understand the rest of the test code.
  • A test should not depend on database access, file access, or any long running task. Rather, an appropriate test double should isolate the external dependencies.
  • A test result should be consistent and time-and-location transparent. A test should not fail if it is executed at midnight, or it should not fail if it is executed in a different time zone.
  • Tests should be meaningful. A class can have getter and setter methods; you should not write tests for the getters and setters because they should be tested in the process of other more meaningful tests. If they're not, then either you're not testing the functionality or your getters and setters aren't being used at all; so, they're pointless.
  • Tests are system documentation. Tests should be readable and expressive; for example, a test that verifies the unauthorized access could be written as testUnauthorizedAccess() or rather when_an_unauthorized_user_accesses_the_system_then_raises_secuirty_error(). The latter is more readable and expresses the intent of the test.
  • Tests should be short and tests should not be treated as second-class citizens. Code is refactored to improve the quality; similarly, unit tests should be refactored to improve the quality. A test class of 300 lines is not maintainable; we can rather create new test classes, move the tests to the new classes, and create a maintainable suite.

As per the preceding best practices, a test should be executed as fast as possible. Then what should you do if you need to test data access logic or file download code? Simple, do not include the tests in an automated test suite. Consider such tests as slow tests or integration tests. Otherwise, your continuous integration cycle will run for hours. Slow tests should still be automated. However, they may not run all the time, or rather they should be run out of the continuous integration feedback loop.

You cannot automate a unit test if your API class depends on slow external entities, such as data access objects or JNDI lookup. Then, you need test doubles to isolate the external dependencies and automate the unit test.

The next section covers test doubles.

Understanding test doubles

We all know about stunt doubles in movies. A stunt double or dummy is a trained replacement used for dangerous action sequences in movies, such as a fight sequence on the top of a burning train, jumping from an airplane, and so on, mainly fight scenes. Stunt doubles are used to protect the real actors, are used when the actor is not available, or when the actor has a contract to not get involved in stunts.

Similarly, sometimes it is not possible to unit test the code because of the unavailability of the collaborator objects, or the cost of interaction and instantiation of collaborators. For instance, when the code is dependent on database access, it is not possible to unit test the code unless the database is available, or when a piece of code needs to send information to a printer and the machine is not connected to a LAN. The primary reason for using doubles is to isolate the unit you are testing from the external dependencies.

Test doubles act as stunt doubles. They are a skilled replacement of the collaborator objects and allow you to unit test code in isolation from the original collaborator.

Gerard Meszaros coined the term test doubles in his book xUNIT TEST PATTERNS, Addison-Wesley—this book explores the various test doubles and sets the foundation for Mockito.

Test doubles can be created to impersonate collaborators and can be categorized into the types, as shown in the following diagram:

Understanding test doubles

Using dummy objects

In movies, sometimes a double doesn't perform anything; they just appear on the screen. One such instance would be standing in a crowded place where the real actor cannot go, such as watching a soccer match or tennis match. It will be very risky for the real actor to go to a full house, but the movie's script needs it.

Likewise, a dummy object is passed as a mandatory parameter object. A dummy object is not directly used in the test or code under test, but it is required for the creation of another object required in the code under test. Dummy objects are analogous to null objects, but a dummy object is not used by the code under test. Null objects (as in the pattern) are used in the code under test and are actively interacted with, but they just produce zero behavior. If they weren't used, you'd just use an actual null value. The following steps describe the usage of dummy objects:

Note

In this book, we will write the code and JUnit tests in the Eclipse editor. You can download Eclipse from the following URL:

https://www.eclipse.org/downloads

  1. Launch Eclipse and create a workspace, \PacktPub\Mockito_3605OS\; we'll refer to it as <work_space> in the next steps/chapters.
  2. We'll create an examination grade system. The program will analyze the aggregate of all the subjects and determine the grade of a student. Create a Java project named 3605OS_TestDoubles. Add an enum Grades field to represent a student's grades:
    package com.packt.testdoubles.dummy;
    
    public enum Grades {
       Excellent, VeryGood, Good, Average, Poor;
    }

    Tip

    Downloading the example code

    You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

    We'll use src as our source code's source folder and test as our test code's source folder. All Java files for this example will be created under the com.packt.testdoubles.dummy package.

  3. Create a Student class to uniquely identify a student:
    public class Student {
    
      private final String roleNumber;
      private final String name;
    
      public Student(String roleNumber, String name) {
        this.roleNumber = roleNumber;
        this.name = name;
      }
    
      //setters are ignored
    
    }
  4. Create a Marks class to represent the marks of a student:
    public class Marks {
    
      private final Student student;
      private final String subjectId;
      private final BigDecimal marks;
    
      public Marks(Student student, String subjectId, BigDecimal marks) {
        this.student = student;
        this.subjectId = subjectId;
        this.marks = marks;
      }
      //getters methods go here 
    }

    Note that the Marks constructor accepts a Student object to represent the marks of a student. So, a Student object is needed to create a Marks object.

  5. Create a Teacher class to generate a student's grades:
    public class Teacher {
    
      public Grades generateGrade(List<Marks> marksList) {
    
        BigDecimal aggregate = BigDecimal.ZERO;
    
        for (Marks mark : marksList) {
        aggregate = aggregate.add(mark.getMarks());
      }
    
      BigDecimal percentage = calculatePercent(aggregate, marksList.size());
    
      if (percentage.compareTo(new BigDecimal("90.00")) > 0) {
        return Grades.Excellent;
      }
    
      if (percentage.compareTo(new BigDecimal("75.00")) > 0) {
        return Grades.VeryGood;
      }
    
      if (percentage.compareTo(new BigDecimal("60.00")) > 0) {
        return Grades.Good;
      }
    
      if (percentage.compareTo(new BigDecimal("40.00")) > 0) {
        return Grades.Average;
      }
    
      return Grades.Poor;
    }
    
    private BigDecimal calculatePercent(BigDecimal aggregate,int numberOfSubjects) {
      BigDecimal percent = new BigDecimal(aggregate.doubleValue()/ numberOfSubjects);
      return percent;
    }
  6. Create a DummyStudent class and extend the Student class. This is the dummy object. A dummy object will be the one that is not the real implementation and provides zero functionality or values. The DummyStudent class throws a runtime exception from all the methods. The following is the body of the DummyStudent class:
    public class DummyStudent extends Student {
    
      protected DummyStudent() {
        super(null, null);
      }
    
      public String getRoleNumber() {
        throw new RuntimeException("Dummy student");
      }
    
      public String getName() {
        throw new RuntimeException("Dummy student");
      }
    
    }

    Note that the constructor passes NULL to the super constructor and throws a runtime exception from the getRoleNumber() and getName() methods.

  7. Create a JUnit test to verify our assumption that when a student gets more than 75 percent (but less than 90 percent) in aggregate, then the teacher generates the grade as VeryGood, creates a DummyStudent object, and passes it as Student to the Marks constructor:
    public class TeacherTest {
    
      @Test public void when_marks_above_seventy_five_percent_returns_very_good() {
      DummyStudent dummyStudent = new  DummyStudent();
    
      Marks inEnglish = new Marks(dummyStudent, "English002", new BigDecimal("81.00"));
    
      Marks inMath = new Marks(dummyStudent, "Math005", new BigDecimal("97.00"));
    
      Marks inHistory = new Marks(dummyStudent, "History007, new BigDecimal("79.00"));
      List<Marks> marks = Arrays.asList(inHistory, inMaths, inEnglish);
    
      Grades grade = new Teacher().generateGrade(marks);
      assertEquals(Grades.VeryGood, grade);
      }
    }

    Note that a DummyStudent object is created and passed to all the three Marks objects, as the Marks constructor needs a Student object. This dummyStudent object is not used in the Teacher class or test method, but it is necessary for the Marks object. The dummyStudent object shown in the preceding example is a dummy object.

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.

Exploring a test spy

A spy secretly obtains the information of a rival or someone very important. As the name suggests, a spy object spies on a real object. A spy is a variation of a stub, but instead of only setting the expectation, a spy records the method calls made to the collaborator. A spy can act as an indirect output of the unit under test and can also act as an audit log.

We'll create a spy object and examine its behavior; the following are the steps to create a spy object:

  1. Launch Eclipse, open <work_space>, and go to the 3605OS_TestDoubles project.
  2. Create a com.packt.testdoubles.spy package and create a StudentService class. This class will act as a course register service. The following is the code for the StudentService class:
    public class StudentService {
    
      private Map<String, List<Student>> studentCouseMap = new HashMap<>();
    
      public void enrollToCourse(String courseName,Student student){
        List<Student> list = studentCouseMap.get(courseName);
        if (list == null) {
          list = new ArrayList<>();
        }
    
        if (!list.contains(student)) {
          list.add(student);
        }
        studentCouseMap.put(courseName, list);
      }
    
    }

    The StudentService class contains a map of the course names and students. The enrollToCourse method looks up the map; if no student is enrolled, then it creates a collection of students, adds the student to the collection, and puts the collection back in the map. If a student has previously enrolled for the course, then the map already contains a Student collection. So, it just adds the new student to the collection.students list.

  3. The enrollToCourse method is a void method and doesn't return a response. To verify that the enrollToCourse method was invoked with a specific set of parameters, we can create a spy object. The service will write to the spy log, and the spy will act as an indirect output for verification. Create a spy object to register method invocations. The following code gives the method invocation details:
    class MethodInvocation {
    
      private List<Object> params = new ArrayList<>();
      private Object returnedValue = null;
      private String method;
    
      public List<Object> getParams() {
        return params;
      }
    
      public MethodInvocation addParam(Object parm){
        getParams().add(parm);
        return this;
      }
    
      public Object getReturnedValue() {
        return returnedValue;
      }
    
      public MethodInvocation setReturnedValue(Object returnedValue) {
        this.returnedValue = returnedValue;
        return this;
      }
    
      public String getMethod() {
        return method;
      }
    
      public MethodInvocation setMethod(String method) {
       this.method = method;
        return this;
      }
    }

    The MethodInvocation class represents a method invocation: the method name, a parameter list, and a return value. Suppose a sum() method is invoked with two numbers and the method returns the sum of two numbers, then the MethodInvocation class will contain a method name as sum, a parameter list that will include the two numbers, and a return value that will contain the sum of the two numbers.

    Note

    Note that the setter methods return this(MethodInvocation). This coding approach is known as builder pattern. It helps to build an object in multiple steps. Java StringBuilder is an example of such a use:

    StringBuilder builder = new StringBuilder();
    builder.append("step1").append("step2")…

    The following is the spy object snippet. It has a registerCall method to log a method call instance. It has a map of strings and a List<MethodInvocation> method. If a method is invoked 10 times, then the map will contain the method name and a list of 10 MethodInvocation objects. The spy object provides an invocation method that accepts a method name and returns the method invocation count from the invocationMap class:

    public class StudentServiceSpy {
      private Map<String, List<MethodInvocation>> invocationMap = new HashMap<>();
    
      void registerCall(MethodInvocation invocation) {
        List<MethodInvocation> list = invocationMap.get(invocation.getMethod());
        if (list == null) {
          list = new ArrayList<>();
        }
        if (!list.contains(invocation)) {
          list.add(invocation);
        }
    
        invocationMap.put(invocation.getMethod(), list);
      }
    
      public int invocation(String methodName){
        List<MethodInvocation> list = invocationMap.get(methodName);
        if(list == null){
          return 0;
        }
    
        return list.size();
      }
    
      public MethodInvocation arguments(String methodName, int invocationIndex){
        List<MethodInvocation> list = invocationMap.get(methodName);
        if(list == null || (invocationIndex > list.size())){
          return null;
        }
        return list.get(invocationIndex-1);
      }
    }

    The registerCall method takes a MethodInvocation object and puts it in a map.

  4. Modify the StudentService class to set a spy and log every method invocation to the spy object:
      private StudentServiceSpy spy;
      public void setSpy(StudentServiceSpy spy) {
        this.spy = spy;
      }
      public void enrollToCourse(String courseName, Student student) {
        MethodInvocation invocation = new MethodInvocation();
        invocation.addParam(courseName).addParam(student).setMethod("enrollToCourse");
        spy.registerCall(invocation);
    
        List<Student> list = studentCouseMap.get(courseName);
        if (list == null) {
          list = new ArrayList<>();
        }
        if (!list.contains(student)) {
          list.add(student);
        }
    
        studentCouseMap.put(courseName, list);
      }
  5. Write a test to examine the method invocation and arguments. The following JUnit test uses the spy object and verifies the method invocation:
    public class StudentServiceTest {
      StudentService service = new StudentService();
      StudentServiceSpy spy = new StudentServiceSpy();
    
      @Test
      public void enrolls_students() throws Exception {
        //create student objects
        Student bob = new Student("001", "Robert Anthony");
        Student roy = new Student("002", "Roy Noon");
        //set spy
        service.setSpy(spy);
    
        //enroll Bob and Roy
        service.enrollToCourse("english", bob);
        service.enrollToCourse("history", roy);
        //assert that the method was invoked twice
        assertEquals(2, spy.invocation("enrollToCourse"));
    
        //get the method arguments for the first call
        List<Object> methodArguments = spy.arguments
    ("enrollToCourse", 1).getParams();
    
        //get the method arguments for the 2nd call
        List<Object> methodArguments2 = spy.arguments
    ("enrollToCourse", 2).getParams();
    
        //verify that Bob was enrolled to English first
        assertEquals("english", methodArguments.get(0));
        assertEquals(bob, methodArguments.get(1));
    
        //verify that Roy was enrolled to history
        assertEquals("history", methodArguments2.get(0));
       assertEquals(roy, methodArguments2.get(1));
    
      }
    
    }

Getting started with mock objects

A mock object is a combination of a spy and a stub. It acts as an indirect output for a code under test, such as a spy, and can also stub methods to return values or throw exceptions, like a stub. A mock object fails a test if an expected method is not invoked or if the parameters of the method don't match.

The following steps demonstrate the test failure scenario:

  1. Launch Eclipse, open <work_space>, and go to the 3605OS_TestDoubles project.
  2. Create a com.packt.testdoubles.mock package and a StudentService class. This class will act as a course register service. The following is the code for the StudentService class:
    public class StudentService {
    
      private Map<String, List<Student>> studentCouseMap = new HashMap<>();
    
      public void enrollToCourse(String courseName,Student student){
        List<Student> list = studentCouseMap.get(courseName);
        if (list == null) {
          list = new ArrayList<>();
        }
    
        if (!list.contains(student)) {
          list.add(student);
        }
    
        studentCouseMap.put(courseName, list);
      }
    }
  3. Copy the StudentServiceSpy class and rename it as StudentServiceMockObject. Add a new method to verify the method invocations:
    public void verify(String methodName, int numberOfInvocation){
      int actual = invocation(methodName);
      if(actual != numberOfInvocation){
        throw new IllegalStateException(methodName+" was expected ["+numberOfInvocation+"] times but actuallyactaully invoked["+actual+"] times");
      }
    }
  4. Modify the StudentService code to set the mock object, as we did in the spy example:
    private StudentServiceMockObject mock;
    
    public void setMock(StudentServiceMockObject mock) {
      this.mock = mock;
    }
    public void enrollToCourse(String courseName,Student student){
      MethodInvocation invocation = new MethodInvocation();
    
      invocation.addParam(courseName).addParam(student).setMethod("enrollToCourse");
    
      mock.registerCall(invocation);
      …//existing code
    }
  5. Create a test to verify the method invocation:
    public class StudentServiceTest {
      StudentService service = new StudentService();
      StudentServiceMockObject mockObject = new StudentServiceMockObject();
    
      @Test
      public void enrolls_students() throws Exception {
        //create 2 students
        Student bob = new Student("001", "Robert Anthony");
        Student roy = new Student("002", "Roy Noon");
    
        //set mock/spy
        service.setMock(mockObject);
    
        //invoke method twice
        service.enrollToCourse("english", bob);
        service.enrollToCourse("history", roy);
    
        //assert that the method was invoked twice
        assertEquals(2, 
        mockObject.invocation("enrollToCourse"));
    
        //verify wrong information, that enrollToCourse was //invoked once, but actually it is invoked twice
        mockObject.verify("enrollToCourse", 1);
    
      }
    
    }
  6. Run the test; it will fail, and you will get a verification error. The following screenshot shows the JUnit failure output:
    Getting started with mock objects

The Mockito framework provides an API for mocking objects. It uses proxy objects to verify the invocation and stub calls.

Implementing fake objects – simulators

A fake object is a test double with real logic (unlike stubs) and is much more simplified or cheaper in some way. We do not mock or stub a unit that we test; rather, the external dependencies of the unit are mocked or stubbed so that the output of the dependent objects can be controlled or observed from the tests. The fake object replaces the functionality of the real code that we want to test. Fakes are also dependencies, and don't mock via subclassing (which is generally always a bad idea; use composition instead). Fakes aren't just stubbed return values; they use some real logic.

A classic example is to use a database stub that always returns a fixed value from the DB, or a DB fake, which is an entirely in-memory nonpersistent database that's otherwise fully functional.

What does this mean? Why should you test a behavior that is unreal? Fake objects are extensively used in legacy code. The following are the reasons behind using a fake object:

  • The real object cannot be instantiated, such as when the constructor reads a file, performs a JNDI lookup, and so on.
  • The real object has slow methods; for example, a class might have a calculate () method that needs to be unit tested, but the calculate() method calls a load ()method to retrieve data from the database. The load() method needs a real database, and it takes time to retrieve data, so we need to bypass the load() method to unit test the calculate behavior.

Fake objects are working implementations. Mostly, the fake class extends the original class, but it usually performs hacking, which makes it unsuitable for production.

The following steps demonstrate the utility of a fake object. We'll build a program to persist a student's information into a database. A data access object class will take a list of students and loop through the student's objects; if roleNumber is null, then it will insert/create a student, otherwise it will update the existing student's information. We'll unit test the data access object's behavior:

  1. Launch Eclipse, open <work_space>, and go to the 3605OS_TestDoubles project.
  2. Create a com.packt.testdoubles.fake package and create a JdbcSupport class. This class is responsible for database access, such as acquiring a connection, building a statement object, querying the database, updating the table, and so on. We'll hide the JDBC code and just expose a method for the batch update. The following are the class details:
    public class JdbcSupport {
      public int[] batchUpdate(String sql, List<Map<String, Object>> params){
        //original db access code is hidden
        return null;
      }
    }

    Check whether the batchUpdate method takes an SQL string and a list of objects to be persisted. It returns an array of integers. Each array index contains either 0 or 1. If the value returned is 1, it means that the database update is successful, and 0 means there is no update. So, if we pass only one Student object to update and if the update succeeds, then the array will contain only one integer as 1; however, if it fails, then the array will contain 0.

  3. Create a StudentDao interface for the Student data access. The following is the interface snippet:
    public interface StudentDao {
      public void batchUpdate(List<Student> students);
    }
  4. Create an implementation of StudentDao. The following class represents the StudentDao implementation:
    public class StudentDaoImpl implements StudentDao {
    
      public StudentDaoImpl() {
      }
    
      @Override
      public void batchUpdate(List<Student> students) {
    
        List<Student> insertList = new ArrayList<>();
        List<Student> updateList = new ArrayList<>();
    
        for (Student student : students) {
          if (student.getRoleNumber() == null) {
            insertList.add(student);
          } else {
            updateList.add(student);
          }
        }
    
        int rowsInserted = 0;
        int rowsUpdated = 0;
    
        if (!insertList.isEmpty()) {
          List<Map<String, Object>> paramList = new ArrayList<>();
          for (Student std : insertList) {
            Map<String, Object> param = new HashMap<>();
            param.put("name", std.getName());
            paramList.add(param);
          }
    
          int[] rowCount = update("insert", paramList);
          rowsInserted = sum(rowCount);
        }
    
        if (!updateList.isEmpty()) {
          List<Map<String, Object>> paramList = new ArrayList<>();
          for (Student std : updateList) {
            Map<String, Object> param = new HashMap<>();
            param.put("roleId", std.getRoleNumber());
            param.put("name", std.getName());
            paramList.add(param);
          }
    
          int[] rowCount = update("update", paramList);
          rowsUpdated = sum(rowCount);
        }
    
        if (students.size() != (rowsInserted + rowsUpdated)) {
          throw new IllegalStateException("Database update error, expected "   + students.size() + " updates but actual " + (rowsInserted + rowsUpdated));
          }
        }
    
        public int[] update(String sql, List<Map<String, Object>> params) {
          return new JdbcSupport().batchUpdate(sql, params);
        }
    
        private int sum(int[] rows) {
          int sum = 0;
           for (int val : rows) {
             sum += val;
           }
           return sum;
        }
    
      }

    The batchUpdate method creates two lists; one for the new students and the other for the existing students. It loops through the Student list and populates the insertList and udpateList methods, depending on the roleNumber attribute. If roleNumber is NULL, then this implies a new student. It creates a SQL parameter map for each student and calls the JdbcSupprt class, and finally, checks the database update count.

  5. We need to unit test the batchUpdate behavior, but the update method creates a new instance of JdbcSupport and calls the database. So, we cannot directly unit test the batchUpdate() method; it will take forever to finish. Our problem is the update() method; we'll separate the concern, extend the StudentDaoImpl class, and override the update() method. If we invoke batchUpdate() on the new object, then it will route the update() method call to the new overridden update() method.

    Create a StudentDaoTest unit test and a TestableStudentDao subclass:

    public class StudentDaoTest {
      class TestableStudentDao extends StudentDaoImpl{
        int[] valuesToReturn;
        int[] update(String sql, List<Map<String, Object>> params) {
          Integer count = sqlCount.get(sql);
          if(count == null){
            sqlCount.put(sql, params.size());
          }else{
            sqlCount.put(sql, count+params.size());
          }
    
          if (valuesToReturn != null) {
            return valuesToReturn;
          }
    
    
          return valuesToReturn;
        }
      }
    }

    Note that the update method doesn't make a database call; it returns a hardcoded integer array instead. From the test, we can set the expected behavior. Suppose we want to test a database update's fail behavior; here, we need to create an integer array of index 1, set its value to 0, such as int[] val = {0}, and set this array to valuesToReturn.

  6. The following example demonstrates the failure scenario:
    public class StudentDaoTest {
    
      private TestableStudentDao dao;
      private Map<String, Integer> sqlCount = null;
      @Before
      public void setup() {
        dao = new TestableStudentDao();
        sqlCount = new HashMap<String, Integer>();
      }
    
      @Test(expected=IllegalStateException.class)
      public void when_row_count_does_not_match_then_rollbacks_tarnsaction(){
      List<Student>  students = new ArrayList<>();
      students.add(new Student(null, "Gautam Kohli"));
    
      int[] expect_update_fails_count = {0};
      dao.valuesToReturn = expect_update_fails_count;
      dao.batchUpdate(students);
    
    }
  7. Check whether dao is instantiated with TestableStudentDao, then a new student object is created, and the valuesToReturn attribute of the fake object is set to {0}. In turn, the batchUpdate method will call the update method of TestableStudentDao, and this will return a database update count of 0. The batchUpdate() method will throw an exception for a count mismatch.

    The following example demonstrates the new Student creation scenario:

    @Test
    public void when_new_student_then_creates_student(){
      List<Student>  students = new ArrayList<>();
      students.add(new Student(null, "Gautam Kohli"));
    
      int[] expect_update_success = {1};
      dao.valuesToReturn = expect_update_success;
      dao.batchUpdate(students);
    
      int actualInsertCount = sqlCount.get("insert");
      int expectedInsertCount = 1;
      assertEquals(expectedInsertCount, actualInsertCount);
    }

    Note that the valuesToReturn array is set to {1} and the Student object is created with a null roleNumber attribute.

  8. The following example demonstrates the Student information update scenario:
      @Test
      public void when_existing_student_then_updates_student_successfully(){
        List<Student> students = new ArrayList<>();
        students.add(new Student("001", "Mark Leo"));
        int[] expect_update_success = {1};
        dao.valuesToReturn = expect_update_success;
    
        dao.batchUpdate(students);
        int actualUpdateCount = sqlCount.get("update");
        int expectedUpdate = 1;
        assertEquals(expectedUpdate, actualUpdateCount);
      }

    Note that the valuesToReturn array is set to {1} and the Student object is created with a roleNumber attribute.

  9. The following example unit tests the create and update scenarios together. We will pass two students: one to update and one to create. So, update should return {1,1} for the existing students and {1} for the new student.

    We cannot set this conditional value to the valuesToReturn array. We need to change the update method's logic to conditionally return the count, but we cannot break the existing tests. So, we'll check whether the valuesToReturn array is not null and then return valuesToReturn; otherwise, we will apply our new logic.

    The following code snippet represents the conditional count logic:

    class TestableStudentDao extends StudentDaoImpl {
      int[] valuesToReturn;
      int[] update(String sql, List<Map<String, Object>> params) {
    
        Integer count = sqlCount.get(sql);
        if(count == null){
          sqlCount.put(sql, params.size());
        }else{
          sqlCount.put(sql, count+params.size());
        }
    
    
        if (valuesToReturn != null) {
          return valuesToReturn;
        }
    
        int[] val = new int[params.size()];
        for (int i = 0; i < params.size(); i++) {
          val[i] = 1;
        }
    
        return val;
      }
    }

    When valuesToReturn is null, the update method creates an array of the params size and sets it as 1 for each index. So, when the update will be called with two students, the update method will return {1,1}.

    The following test creates a student list of three students, two existing students with roleNumbers and one new student.

    @Test
    public void when_new_and_existing_students_then_creates_and_updates_students() {
      List<Student> students = new ArrayList<>();
      students.add(new Student("001", "Mark Joffe"));
      students.add(new Student(null, "John Villare"));
      students.add(new Student("002", "Maria Rubinho"));
    
      dao.batchUpdate(students);
    
    }

    The following screenshot shows the output of the JUnit execution:

    Implementing fake objects – simulators

Note

Note that it took 0.041 seconds to execute four tests. This is interesting because it's something that you wouldn't easily get if you were using a real database.

Summary

This chapter covered the concept of automated unit tests, the characteristics of a good unit test, and explored tests doubles. It provided the examples of dummy objects, fake objects, stubs, mock objects, and spies.

By now, you will be able to identify the different test doubles and write unit tests using test doubles.

The next chapter, Socializing with Mockito, will focus on getting the reader quickly started with the Mockito framework.

Left arrow icon Right arrow icon

Description

This book is ideal for developers who have some experience in Java application development as well as some basic knowledge of test doubles and JUnit testing. This book also introduces you to the fundamentals of JUnit testing, test doubles, refactoring legacy code, and writing JUnit tests for GWT and web services.

What you will learn

  • Explore test doubles and work with dummy, spy, fake, stub, and mock objects
  • Uncover the Mockito architecture and build a custom mocking framework
  • Mock, stub, and spy external code dependencies using Mockito
  • Practice Behaviordriven Development (BDD) with Mockito
  • Make legacy code testable by mocking and spying dependencies
  • Mock GWT and web service dependencies using Mockito
  • Discover argument captors, inline stubbing, and resetting mock objects

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Oct 24, 2014
Length: 214 pages
Edition : 1st
Language : English
ISBN-13 : 9781783983605
Category :
Tools :

What do you get with a Packt Subscription?

Free for first 7 days. €18.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details

Publication date : Oct 24, 2014
Length: 214 pages
Edition : 1st
Language : English
ISBN-13 : 9781783983605
Category :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 96.97
Mockito Essentials
€24.99
MASTERING UNIT TESTING USING MOCKITO AND JUNIT
€35.99
MOCKITO COOKBOOK
€35.99
Total 96.97 Stars icon
Banner background image

Table of Contents

8 Chapters
1. Exploring Test Doubles Chevron down icon Chevron up icon
2. Socializing with Mockito Chevron down icon Chevron up icon
3. Accelerating Mockito Chevron down icon Chevron up icon
4. Behavior-driven Development with Mockito Chevron down icon Chevron up icon
5. Unit Testing the Legacy Code with Mockito Chevron down icon Chevron up icon
6. Developing SOA with Mockito Chevron down icon Chevron up icon
7. Unit Testing GWT Code with Mockito Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
(4 Ratings)
5 star 0%
4 star 100%
3 star 0%
2 star 0%
1 star 0%
Timothy Bish Mar 11, 2015
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
This book offers a nice quick dive into the Mockito framework for testing your code. It serves as a great introduction for the Mockito novice, covering not only the basic workings of the framework but also many concepts surrounding when and why to use a mocking framework.There are a couple of chapters that spend a bit to much time being tutorials for other topics but I can understand why that might be needed and those can be skimmed a bit if things like GWT development doesn't really interest you. The chapters that do cover core Mockito material are thorough and provide a number of useful tips and tricks. For the Mockito novice this book is a definite must read, but could use more chapters covering more advanced topics surrounding the framework if targeted at seasonal Mockito users.The samples in the book cover the topic well and work which is a pleasant surprise these days.
Amazon Verified review Amazon
Alexandros Koufoudakis Jan 25, 2015
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
Mockito Essentials is a good introductory book for those who start using Mockito and for those, who start their journey with unit testing in general.The book is mainly focused on Mockito framework. It starts with quite a detailed introduction in what unit testing and test doubles are. Then, it gradually introduces features of the Mockito framework, starting with simple cases and finishing with special techniques to unit test SOAs and applications based on GWT. I guess those technologies were chosen to demonstrate how you can use the framework in the real life. Somewhere in between you will find the tips and tricks, along with explanations, on how Mockito can improve your unit testing techniques. You will also find two very useful chapters. One of them is focused on behavior driven development and how you can apply Mockito to carry out BDD. Another describes how one can use Mockito to unit test their legacy code. Both chapters are very helpful and a must read for this book, along with the chapter one. Of course, to understand how Mockito works, you have to read chapters two and three.The book also includes working code examples, although I don’t agree with the approach of some of those examples.I would recommend Mockito Essentials not only, because it is a good read, but also, because it one of the best books I’ve read about Mockito framework.
Amazon Verified review Amazon
Dustin Marx Jan 06, 2015
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
"Mockito Essentials" consists of seven chapters and approximately 190 substantive pages. Chapter 1 is not specific to Mockito, but provides an overview of test doubles and explains why they are important in effective unit tests. Chapters 2 and 3 cover "core" Mockito concepts and syntax. Chapter 5 covers using Mockito with legacy applications, using PowerMock, and refactoring legacy code so that it can be mocked with Mockito. Chapter 4 covers use of Mockito with Behavior-Driven Development (BDD). Chapter 6 discusses generation of REST-based web services with JAX-RS and SOAP-based web services with JAX-WS and how to test them with Mockito. Chapter 7 demonstrates building of an application with Google Web Toolkit and Spring MVC and testing it with Mockito and PowerMockito.The chapters on web services and GWT spend a lot of time covering how to develop the sample applications before showing how to test them and this may or may not be desirable to readers. Chapters 1, 2, 3, and 5 could be considered "core" Mockito chapters that one might expect from a book on "Mockito Essentials." Chapter 5 is of most interest for those practicing or interested in practicing BDD. Chapter 6 is of most interest to those developing web services and Chapter 7 is of most interest to those developing Google Web Toolkit applications.The PDF version of "Mockito Essentials" has code listings with black text on white background with no color syntax or line numbers. Some screen snapshots and graphics are in color and some are grayscale. I liked that there were numerous links to additional online resources provided.Overall, I liked the PDF edition of Mockito Essentials that I reviewed. Although most of the text is clear, there were enough typos and awkward sentences to lead me to believe that another round of edits could have been beneficial. The Mockito project provides good, clear documentation (I especially like its API documentation), but "Mockito Essentials" goes past that and combines other related and peripheral technologies in the discussion.
Amazon Verified review Amazon
A. Iqbal Dec 27, 2014
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
I really have enjoyed this book.The writing is clear most of the time, and easy to follow. The chapters that i was really looking forward to reading were, BDD with Mockito and testing legacy code with Mockito. I think the chapters provide a good insight and good foundation on using Mockito for the purpose in hand, however I would have liked more depth and more chapters.But for what i got from the book, i would definitly reccommed if your starting out with Mockito.Overall a good easy read.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.