Blog-Archiv

Montag, 28. Oktober 2019

Spreadsheet with Java Apache POI

This article is about how to create a spreadsheet with the Apache POI 4.1.1 Java library. There is also a "Busy Developers' Guide" available on its home page. Mind that POI contains much more than just a spreadsheet API (MS Word, PowerPoint, ...).

Here is a screenshot of what I'm gonna present source code for:

In first line, the sheet renders the number PI 3 times in different formats. In second line it displays today's date in two different formats, and a text "End of Test". There are no formulas here, this is just about creation and formatting.

Following is the example project's Maven pom.xml, containing the library-dependency.

<project 
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>fri.poi</groupId>
  <artifactId>poiTest</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>4.1.1</version>
    </dependency>

  </dependencies>

</project>

And here is the Java source for it. Explanations see below. Mind that this code needs at least Java 7.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import java.io.*;
import java.util.Date;
import org.apache.poi.ss.usermodel.*;

/**
 * Usage of Apache POI: create a spreadsheet
 * with number-, date- and string-cells.
 */
public class ExcelExample 
{
    public static void main(String[] args) throws Exception    {
        new ExcelExample().create();
    }

    public Workbook create() throws Exception {
        final Workbook workbook = WorkbookFactory.create(false);  // false: use HSSF, else ClassNotFoundException
        final DataFormat format = workbook.createDataFormat();
        
        // create contents
        final Sheet sheet = workbook.createSheet("First Tab");
        
        final Row row0 = sheet.createRow(0);
        
        final Cell cell00 = row0.createCell(0);
        // cell00.setCellType(CellType.NUMERIC); // deprecated!
        cell00.setCellValue(Math.PI);
        
        final Cell cell01 = row0.createCell(1);
        cell01.setCellValue(Math.PI);
        cell01.setCellStyle(workbook.createCellStyle());    // replace default by individual cell-style
        cell01.getCellStyle().setDataFormat(format.getFormat("0.0000"));
        
        final Cell cell02 = row0.createCell(2);
        cell02.setCellValue(Math.PI);
        cell02.setCellStyle(workbook.createCellStyle());
        cell02.getCellStyle().setDataFormat(format.getFormat("0.00"));
        
        final Row row1 = sheet.createRow(1);
        
        final Cell cell10 = row1.createCell(0);
        cell10.setCellValue(new Date());
        cell10.setCellStyle(workbook.createCellStyle());
        cell10.getCellStyle().setDataFormat(format.getFormat("yyyy-dd-MM"));
        
        final Cell cell11 = row1.createCell(1);
        cell11.setCellValue(new Date());
        cell11.setCellStyle(workbook.createCellStyle());
        cell11.getCellStyle().setDataFormat(format.getFormat("dd.MM.yyyy"));
        
        final String END_OF_TEST = "End of Test";
        final Cell cell12 = row1.createCell(2);
        cell12.setCellValue(END_OF_TEST);
        
        //sheet.autoSizeColumn(0);  // doesn't always work well
        sheet.setColumnWidth(0, 30 * 256);  // 30 characters wide
        sheet.setColumnWidth(1, 20 * 256);  // 20 characters wide
        sheet.setColumnWidth(2, END_OF_TEST.length() * 256);
        
        workbook.close();
        final File excelFile = new File("ExcelExample.xlsx");
        try (OutputStream outputStream = new FileOutputStream(excelFile))  {
            workbook.write(outputStream);
            System.out.println("Created file: "+excelFile.getAbsolutePath());
        }
        return workbook;
    }
}

A Workbook is what we would call "Excel Sheet". A Sheet is one tab inside such a workbook, having rows numbered from 0 to n, and any row can have cells numbered from 0 to n. There can be undefined rows and cells anywhere in between. Cells contain a style, by default this is just one global style, but you can also set specific styles into them. Inside the style you can have a format mask for the cell content.

Line 16 creates a Workbook in an implementation-agnostic way by retrieving it from a factory. The boolean parameter lets me choose between

  1. XSSF (XML-format, since 2007, extension .xslx) and
  2. HSSF (old EXCEL format 1997-2007, extension .xsl)

implementations. By default POI comes just with HSSF, so setting true would cause a ClassNotFoundException.

On line 17, I create a data-format to generate cell format masks. This seems to be globally workbook-bound.

Line 20 and 22 show how to create a new sheet with title "First Tab", and a new row in it. This row will contain 3 cells showing the number PI in different formats.

On line 24 the first cell is generated, by means of its owner row. I don't put any format on it, just the value, let's see the outcome. Mind that cell.setCellType(CellType.XXX) is deprecated in newest POI.

Line 28 creates a cell where a format mask should round PI to 4 decimals. It is important to

  • set a newly created style into the cell (see line 30) when the cell needs an individual format.

Else it would use the global style that automatically is in every cell, and all cells would be formatted the same way, derived from the latest setDataFormat() call. On line 31, the individual cell style receives its format mask from the DataFormat object created on line 17.

Well, basically that's it.
On line 33 there is another cell showing PI, this time formatted to just 2 decimals. The subsequent row also contains 3 cells, two dates formatted in different ways, and a final string cell on line 51. Mind that the string automatically got aligned left, while numbers and dates got aligned right.

Before saving the in-memory workbook, I size its columns. There would be just "####" signs displayed in case a content is too big for its column, and that's confusing for users being new to Office tools. When using autoSizeColumn(), this must be done after the cells received their values and formats. But on line 55, I size the columns explicitly. The width is given in 1/256-th of an average character's width. (You should encapsulate this calculation into a method!)

Finally the workbook gets closed and saved to persistence on line 62. The file ExcelExample.xlsx would be located in the working directory of the application, like given by System.getProperty("user.dir"). The try-clause that automatically closes the output-stream is available since Java 7.

This was just a short sneak into POI, showing basic techniques.




Sonntag, 20. Oktober 2019

InjectMocks Annotation to Mock a DAO in a Java Service

This is another Blog about the Mockito Java library. Mocking means simulating, and it is for unit testing.

Test Target

The use case under test is finding a birthday celebration that actually happened. Let's assume such birthdays were stored in a database with their year, but we don't know which was the latest, as celebrations did not happen every year. Thus the business logic is searching back from a given year to find the most recent celebration.

It is this logic that I want to test, and nothing else. I am not interested in testing database access, further I don't want to rely on data stored in a database.

Technically I know that the service uses a DAO, and I know the DAO class so that I can mock it. I also have access to the service implementation class, so that I can construct it. Nevertheless the DAO is a private hidden field in the service. The challenge is to write a unit test, using Mockito, that asserts that the service actually searches backwards for a birthday celebration.

Example Maven Project

Here is the example project's file structure.

The dependencies in Maven pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>fri</groupId>
    <artifactId>mockitoTest3</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.1.0</version>
        </dependency>

    </dependencies>

</project>

Open a terminal window and launch following command to download all dependencies, compile the project and execute the test (Maven must be installed before):

cd dirWherePomXmlIs
mvn package

Java Sources

Following is the service interface. The comment on the findBirthdayCelebration() method describes the business logic. (Mind that methods in interfaces don't need to be declared as public, because everything in a Java interface implicitly is public.)

package fri.mockitoTest.service;

import java.util.Date;

public interface BirthdayCelebrationService
{
    /**
     * @param startYear the year where to start searching backward.
     * @return the most recent year when your birthday celebration actually happened.
     */
    Date findBirthdayCelebration(int startYear);
}

The service implementation contains the business logic that I want to test:

package fri.mockitoTest.service.impl;

import java.util.Date;
import fri.mockitoTest.service.BirthdayCelebrationService;

public class BirthdayCelebrationServiceImpl implements BirthdayCelebrationService
{
    private static final int MAXIMUM_YEARS_BACK = 100;
    
    private BirthdayCelebrationDao dao = new BirthdayCelebrationDao();
    
    public Date findBirthdayCelebration(int startYear) {
        Date found = null;
        int year = startYear;
        for (int count = 0; found == null && count < MAXIMUM_YEARS_BACK; count++)   {
            found = dao.findBirthdayCelebration(year);
            year--;
        }
        return found;
    }
}

Finally here is the DAO, this normally encapsulates persistence access (database):

package fri.mockitoTest.service.impl;

import java.util.Date;

public class BirthdayCelebrationDao
{
    public Date findBirthdayCelebration(int year) {
        // TODO: read from database for given year
        return null;
    }
}

As the DAO will be mocked and this is just a demo example, I did not implement anything here.

The Unit Test

Following is the unit test source code. Explanations see below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package fri.mockitoTest.service;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.Calendar;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import fri.mockitoTest.service.BirthdayCelebrationService;
import fri.mockitoTest.service.impl.BirthdayCelebrationDao;
import fri.mockitoTest.service.impl.BirthdayCelebrationServiceImpl;

/**
 * How to mock a DAO in an existing Service, so that we can
 * test service functionality without needing a real DAO.
 */
public class BirthdayCelebrationServiceTest
{
    @Mock
    private BirthdayCelebrationDao dao;
    
    @InjectMocks
    private BirthdayCelebrationService service = new BirthdayCelebrationServiceImpl();
    
    @Before
    public void initMocks() {
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void whenNoBirthdayCelebrationThenSkipToPreviousYear_Success()  {
        // set up test data
        final int LATEST_BIRTHDAY_CELEBRATION_YEAR = 2017;
        final Date expectedBirthdayCelebration = newBirthdayDate(LATEST_BIRTHDAY_CELEBRATION_YEAR);
        final int START_YEAR = LATEST_BIRTHDAY_CELEBRATION_YEAR + 2;
        
        // set up behavior
        for (int year = START_YEAR; year > LATEST_BIRTHDAY_CELEBRATION_YEAR; year--)
            when(dao.findBirthdayCelebration(year)).thenReturn(null);
        when(dao.findBirthdayCelebration(LATEST_BIRTHDAY_CELEBRATION_YEAR)).thenReturn(expectedBirthdayCelebration);
        
        // take action
        final Date birthdayCelebration = service.findBirthdayCelebration(START_YEAR);
        
        // assert result
        assertEquals(expectedBirthdayCelebration, birthdayCelebration);
    }

    private Date newBirthdayDate(int year) {
        final Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MONTH, Calendar.MAY);
        calendar.set(Calendar.DAY_OF_MONTH, 12);
        calendar.set(Calendar.YEAR, year);
        return calendar.getTime();
    }
}

The static imports from JUnit and Mockito in lines 3 and 4 are for using static methods from these libraries like assertEquals(....) and when(....) without having to always write their class prefix. This makes the code much better readable (in case you know what a static import is:-).

Line 22 and 23 define the DAO mock. When initMocks() on line 29 gets called through the JUnit @Before annotation, Mockito will instantiate a BirthdayCelebrationDao and put it on the private field.

Line 25 and 26 define the mock injection. Line 26 contains a harcoded instantiation of a service object. This must be done when the object is used through an interface, in this case BirthdayCelebrationService. Mockito would not know which concrete class it should use for instatiation. Optionally I could also declare BirthdayCelebrationServiceImpl as field-type and leave out the allocation, Mockito would then allocate the object when processing the @InjectMocks annotation. Thus @InjectMocks is also an implicit @Mock annotation.

After instantiation Mockito initializes the service. For injection it uses all mocks and spys defined so far in this class. This is the way how the mocked DAO from line 23 arrives in the service on line 26.

The test method on line 34 carries the test target in its name. I want to make sure that the service has a search-backwards loop for some year that had a birthday celebration.

Every unit test consists of at least three parts:

  1. Build test data
  2. Carry out the action to test
  3. Assert the action result (to success or failure)

In case we use mocks, we also need to teach them behavior, Mockito calls this "stubbing". We could regard this to be the last part of building test data. Line 36 - 38 defines test data. Line 41 - 43 teaches the DAO mock behavior (stubs the DAO). This is done using the Mockito methods when() and thenReturn(). (We also could remove the for-loop, because Mockito mocks return null by default when nothing was stubbed.)

On line 46 the service gets called. This is the action under test. We ask it to search the most recent birthday celebration back from 2019.

On line 49 we assert that the most recent birthday celebration actually was in 2017, like it was stubbed on line 43. The actual date of the birthday, generated on line 52, does not matter here, just the stub date and the result date must be the same. This proves that the service searched backwards by decrementing the input year.

Mind that this test does not make sure that the service would not go into an endless loop, as it would happen when no MAXIMUM_YEARS_BACK limit was built into BirthdayCelebrationServiceImpl. We would have to write another test method for that!

Conclusion

The @InjectMocks annotation is not easily understandable, but quite useful. We would have to evaluate whether recursive injection works. Its documentation says:

Mockito is not an dependency injection framework, don't expect this shorthand utility to inject a complex graph of objects be it mocks/spies or real objects.



Sonntag, 13. Oktober 2019

UML Diagrams with PlantUML

There is a free Java tool called PlantUML that offers to create UML diagrams through programming in a domain-specific-language, instead of graphically drawing boxes and lines.

I used PlantUML for some months, and had ups and downs with it. The ups took place when drawing sequence diagrams. Downs I had with all other kinds of diagrams when trying to give them good layout.

Simple Example

This is a small class-diagram example, suitable to quickly show how things are done using PlantUML.

@startuml

legend
  An airport can have several terminals.
  A terminal can have several gates.
endlegend

Airport *-{ Terminal
Terminal *-{ Gate

@enduml

Store this text into a file named airport.puml. Then download plantuml.jar, place it into the folder where the file is, and launch following commandline:

java -jar plantuml.jar airport.puml

An image file named airport.png will be generated, and you can view it through any image viewer.

A more convenient way to work with plantuml.jar is following command:

java -jar plantuml.jar -gui

This starts a graphical user interface that will list all .puml files in directory. Moreover it will immediately generate an image for every newly created file. Double-clicking onto an list item will open a built-in image viewer that displays the diagram in a new window.

This is how the class-diagram above looks like:

Try it out by yourself at the online-server.

Vertical Variant

If you want the boxes to be vertically arranged, you must increase the edge lengths (!) by adding a "-":

@startuml

Airport *--{ Terminal
Terminal *--{ Gate

@enduml

Result:

Styling

Of course you can customize colors and lines using PlantUML skinparams. Names of skinparams can be listed using following command:

java -jar plantuml.jar -language

Comments

Commenting out lines can be done via quotes:

@startuml

' single line comment

/' multiline
  comment '/

@enduml

More Complex Example

Following is a component-diagram that describes how to use an airport for departure.

@startuml

legend
  Check-in starts about 3 hours before take-off.
  Mind that automated check-in will not accept your non-cabin baggage,
  and you may want to get rid of that because it contains your camping knife!
  Airports differ in where and when they check passports, boarding card
    and cabin baggage.
  Boarding (at the gate) starts about 60 - 30 minutes before and normally
    includes an accurate cabin baggage check (knives will be confiscated).
  Boarding closes 20 minutes before flight.
  State officials that check passports will **not** help you find your gate!
  
  **Unchecked (cabin) baggage**: what you take into the aircraft and put into
    the rack, 7 kg, must not contain a knife or liquids, can contain a laptop.
  **Checked (hold) baggage**: what you give to the airline on check-in, 23 kg,
    may contain a knife, don't put laptop into it.
  Baggage must not exceed 55 x 38 x 20 cm.
  Damaged baggage must be reported (written) within 7 days, delayed baggage
    within 21 days.
endlegend

skinparam rectangle {
 roundCorner 30
 borderColor gray
}

package "Airport A" {

  package "Terminal 1" {
  
    rectangle "Public Access / Departure" as publiczone {
      "**Check-in**:\nShow passport and ticket,\nassign seat,\nleave hold baggage,\nget boarding card" as check_in

      check_in -> [Airline C]
      [Airline B]
      [Airline A]
    }
   
    rectangle "Transit Zone (passengers only)" as transitzone {
      stack "Duty-free shopping"
      
      package "Gate A1" as gate {
        "Cabin baggage check,\nshow boarding card\nand passport" as board
      }
    }
    
    publiczone ==|> transitzone: Say Goodbye, you're leaving the country.\nShow boarding card and passport.
    
    queue "Aircraft for Flight ABC-123" as flight
    board ==|> flight: Show boarding card, passport.
    
  }
}

@enduml

Here is how the departure diagram looks like:

You see that I used some tricks like inline text formatting (bold font, newlines) and skin-parameters (to style the inner boxes).

Now this is quite acceptable I think, although the red edges are much too long, and they touch the text. And - why is the legend on bottom when I wrote it on top?

Anyway, let's create another diagram for arrival:

@startuml

legend
  In case an immigration form is required, make sure you filled one
  before standing in long lines at arrival passport checks,
  because you may be sent back to fill one and then again stand in line!
endlegend

skinparam rectangle {
 roundCorner 30
 borderColor gray
}

package "Airport B" {

  package "Terminal 2" {
  
    queue "Aircraft for Flight ABC-123" as flight

    rectangle "Transit Zone" as transitzone {
      "With cabin baggage" as unboard
    }
   
    flight -> unboard
   
    rectangle "Arrival" as arrival {
      "Get hold baggage" as passenger
    
      passenger <- [Hold luggage conveyor for Flight ABC-123]
    }
   
    transitzone ==|> arrival: Show boarding card, passport,\nmaybe immigration form.\nYou are entering the country.

    rectangle "Public Access / Arrival" as publiczone {
      "City Train"
      "Taxi"
    }
   
    arrival ==|> publiczone: Customs, then say Hello!
  }
}

@enduml

Here is how the arrival looks like:

This is not acceptable. Look at the edges. Why do they bend so ugly? Why don't they go straight down?

Here the PlantUML weakness shows up. It is the automatic layout done by GraphViz, which is a native library, not Java. The way how PlantUML and GraphViz work together is not the best one.

Now you may say "It is the edges that are too long!". So let's make them shorter by taking away a "=":

@startuml
....

    transitzone =|> arrival: Show boarding card, passport,\nmaybe immigration form.\nYou are entering the country.

    ....
   
    arrival =|> publiczone: Customs, then say Hello!

....
@enduml

I don't want to show the result. The diagram now expands horizontally, the edges are cluttered, obscuring the text. You can try it out by yourself. It is not acceptable.

The only way I could improve this is by adding following instruction:

@startuml

skinparam linetype ortho

....

@enduml

Then it looked like this:

Still the red edges are much too long and unnecessarily crooked. The first one looks like it is not related at all to its text, the second one obscures it.

Layout with PlantUML is really time-consuming. You have to find out the techniques that PlantUML built in to manipulate GraphViz. Moreover I uncovered platform differences, the diagram looked differently when rendered on WINDOWS, LINUX or at the online-server, or when rendered with different versions of GraphViz and PlantUML.

Conclusion

Use PlantUML for sequence diagrams, here it shines. This is because PlantUML doesn't use GraphViz for it. Don't use PlantUML for anything complex unless you want your readers shake their heads and skip your diagram.




Sonntag, 6. Oktober 2019

Mockito for Java Unit Tests

Unit testing is expected to cover the smallest units of work, in Java this means classes and packages. Integration testing would cover the combination of modules. Regression testing is for maintaining quality after fixes and changes.
This Blog is about unit testing with "mocks", which are objects that simulate some environment like a database or a remote server. Mocks are useful for isolating tests from their environment that may be too unreliable or too slow for periodic automated tests.

The Mockito library has turned out to be the most popular helper for Java unit tests. Its responsibility is simulation, not assertion or running tests like JUnit does, although there is some overlapping.

I use the old JUnit 4 version for the examples in this Blog. JUnit 5 ("Jupiter") introduces new classes, methods and annotations.

Terms

Before we can try out Mockito we have to learn new words (or old words like "stub" in a new sense):

  • Mock:
    Object that simulates another object of which it knows the class or interface, but not its behavior, this must be "stubbed"

  • Spy:
    Object that copies a given object, or instantiates a given class, and forwards all calls to that object, so it simulates also its behavior; the behavior additionally can be "stubbed"

  • Captor:
    Helper that is able to reveal arguments passed to some mock method

  • to stub:
    To teach a mock or spy how to behave when some method is called on it, i.e. using when(), given(), doReturn(), doAnswer(), doThrow(), ...

  • to verify:
    To check whether a method was called with certain arguments, never, once (default), or a given number of times

Usage

Create a Maven project structure

with following pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>fri</groupId>
    <artifactId>mockitoTest3</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.1.0</version>
        </dependency>

    </dependencies>

</project>

Then compile the project:


cd mockitoTest
mvn package

Then import it into your preferred IDE. For Eclipse you should either import it as "Maven Project" or launch mvn eclipse:eclipse before importing it as "Java Project".

Preparations

If you want to use the annotations of Mockito, you need to let Mockito read them by either a before-method:

import org.junit.Before;
import org.mockito.MockitoAnnotations;
import org.mockito.Mock;

public class MyMockitoTest
{
    @Before
    public void initMocks() {
        MockitoAnnotations.initMocks(this);
    }

    @Mock
    private  List<String> mockedList;
    
    ....
}

or by defining a JUnit rule:

import org.junit.Rule;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.Mock;

public class MyMockitoTest
{
    @Rule
    public MockitoRule rule = MockitoJUnit.rule();

    @Mock
    private  List<String> mockedList;

    ....
}

Annotations can be used outside method implementations. You don't need any initializer if you prefer to call Mockito explicitly like this:

import java.util.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Test;

public class MyMockitoTest
{
    @Test
    public void whenMocking() {
        List<String> mockedList = mock(List.class);
        ....
    }
}

Annotations are useful sometimes, but currently they are overused. Good source code is short, concise, and quickly readable, and annotations always need additional documentation reading. I would stick to static imports (like above) and explicit calls. That way you have everything in place.

Example Tests

All following tests have been written and checked to succeed (be green).

Mock

This example shows what a Mockito mock can do (without claim to completeness!). Copy & paste it into the class above.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @Test
    public void whenMocking() {
        List<String> mockedList = mock(List.class);

        // call a method
        mockedList.add("one");
        // verify that the method has been called once
        verify(mockedList, times(1)).add("one");
        // why would I want to verify that a method has been called when I call it myself?
        
        // the mock doesn't behave like a list!
        assertNull(mockedList.get(0));
        assertEquals(0, mockedList.size());

        // we must teach a mock how to behave
        when(mockedList.size()).thenReturn(100);
        assertEquals(100, mockedList.size());
        
        when(mockedList.get(0)).thenReturn("zero");
        assertEquals("zero", mockedList.get(0));
        
        when(mockedList.get(1)).thenReturn("one");
        assertEquals("one", mockedList.get(1));
    }

Line 3 creates a mock object that simulates a List interface. In line 6 we use it like a list by calling add("one"). After that we call Mockito's verify() to make sure that the method has been called once.

Now we would expect that the list mock contains "one", but line 12 asserts that it doesn't, the list is empty. This is because the mock can't know the behavior of a List, we must teach it ("stub" it).

First we teach it in line 16 to always behave like it contains 100 items. In line 19 we teach it to always return "zero" when we call get(0) on it, the same in line 22 with "one".

As we see, the add() and get() methods are not related in any way, like it would be with a real List behavior. A mock is a very stupid thing.

Spy

Spies are a bit better than mocks. We don't need to teach them everything.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    @Test
    public void whenSpying() {
        final List<String> realList = new ArrayList<>();
        List<String> spiedList = spy(realList);

        // call a method
        spiedList.add("one");
        // verify that the method has been called once
        verify(spiedList, times(1)).add("one");
        // why would I want to verify that a method has been called when I call it myself?
        
        // the spy behaves like a list!
        assertEquals("one", spiedList.get(0));
        assertEquals(1, spiedList.size());

        // but the real list is not affected, the spy works on a copy!
        assertEquals(0, realList.size());
        
        // we can teach a spy how to behave
        when(spiedList.size()).thenReturn(100);
        assertEquals(100, spiedList.size());
        
        when(spiedList.get(0)).thenReturn("zero");
        assertEquals("zero", spiedList.get(0));
        
        // when(spiedList.get(1)).thenReturn("one");
        // this would throw an IndexOutOfBoundsException, because we
        // can not teach the spy something the real object can't do yet
        spiedList.add("two");
        assertEquals("two", spiedList.get(1));
        // now we can stub the call
        when(spiedList.get(1)).thenReturn("one");
        assertEquals("one", spiedList.get(1));
    }

A spy is a kind of proxy, with the difference that it copies its "real" object. In line 3 we create a real List object, in line 4 we wrap it into a spy. Again we call add("one"), and verify that it has been called after.

Mind that verify() is an assertion, and that it checks technical logic that may change frequently. Verifing makes sense in certain cases. In this example I kept it to show that a spy can be treated like a mock, not because I recommend to do it all the time and everywhere.

So, where is the difference to mock? We see it in ine 13. Here we assert that the spy list contains what we have added before. And we did not not teach (stub) it.

In line 17 we see that the real list is still empty. The spy uses its own private copy of the real object!

What more? In line 20 we stub the spy. We teach it to always return 100 for its size, although its real object would return 1 now. Line 21 asserts that this works. We can manipulate the behavior of the list through stubbing in any way. Line 23 makes it return a wrong list item for index 0. Whatever consequences this has on the adopted behavior.

In line 26 there is another difference to a mock. We can not teach it calls that are impossible to do on the wrapped real object, in this case a call to get(1). There is only one item in the wrapped list, so this causes an exception. First we must add another item, then we can stub get(1), like done in line 29 and 32.

A spy is a mock with behavior, additionally the behavior can be modified (stubbed). We should prefer the original behavior to stubbing, because we may be better off with a mock when needing lots of stubs on a spy.

Captor

This example is to demystify the word "captor".

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    @Test
    public void whenCapturing() {
        List<String> mockedList = mock(List.class);
        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);

        mockedList.add("one");
        mockedList.add("two");
        
        verify(mockedList, times(2)).add(captor.capture());
        List<String> arguments = captor.getAllValues();
        assertEquals("one", arguments.get(0));
        assertEquals("two", arguments.get(1));
    }

Line 3 allocates a mock. We create a "captor" object for argument verification in line 4.

The lines 6 and 7 call the mocked add() method two times, with different parameters. To find out now what were the parameters, we need to call verify() like done in line 9, and pass the captor instead of real parameter values. After that, we can retrieve all parameter values from the captor, and assert them.

Conclusion

This was a glimpse into Mockito. Stubbing would need more examples, because this is used heavily. I will do this in subsequent Blogs.