Stubbing and Mocking
Stubbing and Mocking
Contents
Download now
Mocking and stubbing are the cornerstones of having quick and simple unit
tests. Mocks are useful if you have a dependency on an external system, file
reading takes too long, the database connection is unreliable, or if you don’t
want to send an email after every test.
Unlike with integration or functional tests, where the real system is being tested
as a whole, unit tests should focus on a single class. Everything else should be
either a simple class or a mock. Mockito now seems to be the dominant Mocking
framework in Java, and is now in its second version. Spock is also a great
solution that we will explore in a future article.
We will cover:
1. Downloading and setting up of Mockito — the most popular Java framework
for mocking/stubbing,
2. Some brief theory on when and why you want to use Mockito,
3. Stubbing return data from methods,
4. Verifying interactions,
5. Using argument captors, and
6. Returning custom mocked responses.
Despite the version number, Mockito is mostly the same as Mockito 1, as far as
the external API is concerned. If you have any existing Mockito tests, you may
need to change some imports. However, other than that, upgrading to Mockito
should be easy regardless of your code size.
Prerequisites
It is assumed that you already have a Java project and you want to write unit
tests for it. We will need:
A sample Java project constructed in a well-disciplined manner, i.e. using
Dependency Injection,
A valid pom.xml file that builds the project,
Maven installed—the command mvn should be available in your command line,
and
Internet access to download Maven dependencies.
Mockito is commonly used with JUnit. Therefore, familiarity with JUnit is
essential. It is also assumed that we already know our way around basic Maven
builds. If not, then feel free to consult its official documentation first.
Setting Up Mockito
Mockito is already distributed via Maven central, so using it in a Java forward is
a painless process. We need to modify the pom.xml:
<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.2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@PersistenceContext
private EntityManager entityManager;
This class reads customers from the database via the EntityManager. So, how
do we test this class?
A naive solution would be to pre-fill a real database with customers and run this
test against it. This is problematic for a lot of reasons. It creates a hard
dependency on a running database, and also requires an extra step to create the
test data. For this example, you could make it work, but in a real-world
application, this would not be practical at all.
A better solution would be to use an in-memory database. This would solve the
speed problem, but it would suffer from the extra step of pre-filling the database
with custom test data. Again, in a real-world application, this can get quickly out
of hand, especially when multiple tests also change the initial state of the
database.
The best solution for a true unit test is to completely remove the database
dependency. We will stub the database connection instead, and “fool” our class
to think that it is talking to a real EntityManager, while in reality, the
EntityManager is a Mockito stub. This way, we have complete control over what
is returned by the database connection without having to deal with an actual
database.
Here is the respective unit test:
@Test
public void happyPathScenario(){
Customer sampleCustomer = new Customer();
sampleCustomer.setFirstName("Susan");
sampleCustomer.setLastName("Ivanova");
First, we create a sample customer. We use the real class as this is very simple
and fast, a POJO actually, so no need to mock it.
Then, we mock the Entity Manager. This happens with the mock() static call.
Using an annotation would also work.
The most important line of the whole test is the next one. This defines what will
happen when somebody calls the find() method of the entityManager. We set it
up so that our sample customer is returned, i.e. if 1 is passed as a database ID.
Finally, we create our class under test CustomerReader and pass it our own
mocked Entity Manager as a dependency. For this particular example, we have
added a new setter method to follow setter injection, but this would work with
constructor injection as well.
From that point on, our class under test does not really know that the Entity
Manager is fake. It will just call its method and get the sample Customer
unaware that Mockito is behind everything.
This test satisfies all requirements outlined in the previous section. It has no
external dependencies, it only needs the Java source code, it is very fast and it
is completely deterministic. We did not need a database at all.
The When-Then Mantra
Using simple stubbing directives when(something).thenReturn(somethingElse)
will get you a long way in your unit tests. Depending on your application, this
might be the only Mockito feature you will ever need.
If you have multiple test methods, it makes sense to move the mock creation
process to a single place and only differentiate its behavior for each individual
test.
You might have already noticed that our CustomerReader class is not correct, as
it does not handle the null case, i.e. the given database ID does not exist as an
object in the DB. While we could copy-paste the existing unit test, it is best if we
organize the code with a common test method.
//Class to be tested
private CustomerReader customerReader;
//Dependencies
private EntityManager entityManager;
@Before
public void setup(){
customerReader = new CustomerReader();
entityManager = mock(EntityManager.class);
customerReader.setEntityManager(entityManager);
}
@Test
public void happyPathScenario(){
Customer sampleCustomer = new Customer();
sampleCustomer.setFirstName("Susan");
sampleCustomer.setLastName("Ivanova");
when(entityManager.find(Customer.class,1L)).thenReturn(
@Test
public void customerNotPresentInDb(){
when(entityManager.find(Customer.class,1L)).thenReturn(
Here, we have extracted the common code of creating our test class and its
mocked dependencies into the setup method. The Before annotation will make
this initialization code run for each test method.
The only thing that differs between each test method is the when directive. In the
second case, we make the fake database return null to emulate a customer that
does not exist in the Database.
In true TDD, fashion we have created the unit tests before the actual
implementation. If you run our unit test, the second test method will fail.
As an exercise, feel free to correct the CustomerReader implementation and also
add extra unit tests when the first and/or last name are null themselves.
As you are adding more tests, it will be obvious that not having to deal with a
real database allows you to iterate faster on each subsequent implementation
feature.
Basic Mocking with Mockito
Let’s see a case where a mock is needed instead of a stub, assuming that we
want to test the following class in the same application:
This class has two external dependencies, and we use constructor injection this
time around. It checks for late invoices of customers and sends them an email if
an invoice is late.
In a real system, the InvoiceStorage class is actually a web service that
connects with an external legacy CRM system which is slow. A unit test could
never make use of such a web service.
The EmailSender class is also an external system from a third-party mass email
provider. So, we will mock it as well.
However, as soon as you try to write a unit test for this class, you will notice that
nothing can really be asserted. The method that we want to test – notifyIfLate
– is a void method that cannot return anything. So how do we test it?
In this case, we need to focus on the side effects of the code. The side effect
here is sending an email. This email is sent only if an outstanding invoice is
present. Mockito provides the verify family of directives for testing side-
effects. Here is the whole unit test:
//Class to be tested
private LateInvoiceNotifier lateInvoiceNotifier;
//Test data
private Customer sampleCustomer;
@Before
public void setup(){
invoiceStorage = mock(InvoiceStorage.class);
emailSender = mock(EmailSender.class);
@Test
public void lateInvoice(){
when(invoiceStorage.hasOutstandingInvoice(sampleCustome
lateInvoiceNotifier.notifyIfLate(sampleCustomer);
verify(emailSender).sendEmail(sampleCustomer);
}
@Test
public void noLateInvoicePresent(){
when(invoiceStorage.hasOutstandingInvoice(sampleCustome
lateInvoiceNotifier.notifyIfLate(sampleCustomer);
verify(emailSender, times(0)).sendEmail(sampleCustomer)
}
As before, we stub the InvoiceStorage class using the when/then syntax. For
the first test, we assume the customer has an outstanding invoice. For the
second test, no late invoice is present.
Both tests do not contain the normal JUnit assert statements. Instead, we use
the verify directive which examines the mocks after each run and passes the
test if a method was called with the specified argument. For the second test, we
want to make sure that the email method is NOT called. Therefore, we also add
the times argument to restrict the number of times this method was (not) called.
If times is omitted it is assumed to be 1, which is what we do in the first test.
Notice that mocks can still be stubbed if needed. You can think of mocks as a
superset of stubs and this is why Mockito calls both of them “mocks”.
Verifying Arguments with Argument
Captors
The previous example was relatively simple, we just verified whether a single
method was called or not. Sometimes we need more detail, and instead of
looking only at the method call itself, we are also interested in the arguments.
As an example, let’s assume that the analytics department wants more
extensive metrics and asked you to implement an extra mechanism, where
several important events for a customer are recorded and later analyzed.
Here is the source code of the Event that the metrics solution supports:
//Class to be tested
private LateInvoiceNotifier lateInvoiceNotifier;
//Test data
private Customer sampleCustomer;
@Before
public void setup(){
invoiceStorage = mock(InvoiceStorage.class);
emailSender = mock(EmailSender.class);
eventRecorder = mock(EventRecorder.class);
@Test
public void lateInvoiceEvent(){
when(invoiceStorage.hasOutstandingInvoice(sampleCustome
lateInvoiceNotifier.notifyIfLate(sampleCustomer);
verify(emailSender).sendEmail(sampleCustomer);
}
}
The unit test here examines two related actions. First of all, it verifies that the
email was indeed sent as in the previous section. For the event, we could also
use a second verify directive to check it as well, but instead, we use an
ArgumentCaptor to examine the event in more detail. First, we construct an
ArgumentCaptor and define it as a holder for an Event class. Then, in the verify
directive we use our captor by calling its capture() method.
At this point, when the unit test is complete, the captor contains the exact
argument that was sent to the mocked EventRecorder class when the
notifyIfLate method was called.
We can extract the actual argument instance by calling the getValue() method
of the captor. The result is a normal Java object that can be used for further
assertions using the usual JUnit statements. In the example above, we check
the event type, the fact that the full name is formed correctly, and whether there
is a timestamp.
Note that the argument can be any complex Java object. In our example, it is a
class with scalar values, but you could have nested classes or a whole data
structure/list. Mockito could capture it without any problem, and you could run
any number of assert statements in the final result, or any fields of the argument
class.
Forming Dynamic Responses for Mocks
As a final example of the power of Mockito we will see how you can create
custom responses from mocks that depend on the arguments of the call. This is
an advanced technique that will be needed only for some very specific corner
cases in your unit tests. If you have a choice, it is best to return predefined
results in your mocks/stubs so that the test is more readable. Use dynamic
responses only as a last resort in your unit tests.
Dynamic Manipulation of Arguments
Let’s see an example where we just want to modify the argument itself. Let’s
assume that you want to test the following class:
You should instantly see why writing a unit test for this class is a bit tricky. Even
though the DAO logic itself is very basic, the big problem is the fact that once
the customer is saved using the persist method, its database ID is sent to the
logger. For this contrived example, the code will work just fine in the real system,
as the database will indeed assign an ID to the object as soon as it is saved.
But how can we replicate this processing in our unit test? The persist method
does not return an argument so we cannot mock it with the usual when/then
directives. However, even for this corner case, Mockito still has a solution:
// Class to be tested
private CustomerDao customerDao;
@Before
public void setup() {
customerDao = new CustomerDao();
entityManager = mock(EntityManager.class);
customerDao.setEntityManager(entityManager);
logger = mock(Logger.class);
customerDao.setLogger(logger);
}
@Test
public void happyPath() {
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
Customer customer = invocation.getArgument(0);
customer.setId(123L);
return null;
}
}).when(entityManager).persist(any(Customer.class));
customerDao.saveCustomer("Suzan", "Ivanova");
@Test(expected = IllegalArgumentException.class)
public void missingInformation() {
customerDao.saveCustomer("Suzan", null);
}
This is a class that takes a list of customers and saves them on the
UserRepository. For each customer an event of type REGISTRATION is also
emitted.
We want to test the method called massRegister, as the register one is private.
In theory, we could pass a list of just one customer in our unit test, but in
practice, it is best to try with a big list of customers. The code here is very
simple and does no error checking at all, but in a production system, there might
be several consistency checks before a customer is registered. A realistic unit
test would pass a huge list of customers with various problems so that all
checks can be evaluated during unit testing.
Let’s say we want to test using a list of 20 customers. This time the
saveRepository method does return an argument, so in theory, we could use
the when/then directive 20 times to instruct it exactly what output it should
send.
A more concise way is the following:
//Class to be tested
private MassUserRegistration massUserRegistration;
//Test data
private List<Customer> sampleCustomers;
@Before
public void setup(){
sampleCustomers = new ArrayList<>();
eventRecorder = mock(EventRecorder.class);
userRepository = mock(UserRepository.class);
when(userRepository.saveCustomer(anyString(),
anyString())).thenAnswer(new Answer<Cust
public Customer answer(InvocationOnMock invocation)
String firstName = invocation.getArgument(0);
String lastName = invocation.getArgument(1);
Customer newCustomer = new Customer(firstName,
newCustomer.setFullName(firstName+" "+lastName)
newCustomer.setSince(LocalDate.now());
return newCustomer;
}
});
@Test
public void registerTwentyAccounts(){
sampleCustomers.add(new Customer("Susan", "Ivanova"));
sampleCustomers.add(new Customer("Lyta", "Alexander"));
sampleCustomers.add(new Customer("Vir", "Cotto"));
sampleCustomers.add(new Customer("Stephen", "Frankling"
//[...20 customers redacted for brevity...]
massUserRegistration.massRegister(sampleCustomers);
}
}
$ mvn -B archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeVersion=1.4 \
-DgroupId=com.mycompany.app \
-DartifactId=my-app
Create a new repository on GitHub. In the gitignore selection at the end, pick
Java.
Make an initial push of the code:
$ git init
$ git remote add origin YOUR_GITHUB_REPOSITORY_ADDRESS
$ git pull origin master
$ git add -A
$ git commit -m "initial commit"
$ git push -u origin master
<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.2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
$ mvn test
Initial run
Build Job
We need to customize the starter workflow to build the application in the CI
environment.
Click on the Edit Workflow button on the top-right corner toopen the Workflow
Editor:
checkout
sem-version java 1.8
mvn package
checkout
cache restore
sem-version java 1.8
mvn package
cache store
Cache is a built-in command that stores and retrieves files from a project-wide
cache. Cache is smart and can figure out what files and directories it needs to
store.
Open the Environment Variables section and add the following variable using
+Add env_vars:
MAVEN_OPTS : -Dmaven.repo.local=.m2
Environment Variables can be defined at the block level and are active for all its
jobs. The value maven.repo.local=.m2 tells Maven to download dependencies in
the local directory instead of in the user’s $HOME, so they can be cached more
easily.
Click on Run the Workflow and Start:
Optimized build job
Now, the downloaded packages are preserved in the cache. As a result, the
build job a lot less time.
Automating Tests in Semaphore
Now that we are happy with the build job, let’s add a test block:
Click on Edit the Workflow.
Click the +Add Block dotted box:
Add a block
Name the second block “Tests” and the job “Unit tests”.
Type the Maven test in the command box: mvn test
Open the Prologue section and type the following commands:
checkout
cache restore
sem-version java 1.8
Commands in the prologue are executed before each job in the block.
Once more, open the Environment Variables section and add the same
MAVEN_OPTS variable as last time.
With this simple CI pipeline, from now on, your code is tested on each push.
You can add many more jobs to the test block, for example integration tests,
code analysis, and benchmarks. Semaphore also has Test Reports where you
can see which tests are skipped, find the slowest and failing tests in your test
suite.
Summary
In this tutorial, we have written several unit tests using Mockito for both
stubbing and mocking.
We have seen:
1. How to download and set up Mockito via Maven,
2. The reasons that mocks and stubs are needed,
3. Basic stubbing with the when/then directives,
4. Basic verification with verify and times() directives,
5. Advanced verification of arguments using Mockito Argument Captors,
6. Advanced dynamic manipulation of arguments, and
7. Advanced dynamic responses based on arguments.
8. You can continuously test your code by adding your project to Semaphore and
setting up a CI pipeline.
Learn CI/CD
Level up your developer skills to use CI/CD at its max.
Start Learning
Leave a Reply
Your email address will not be published.
Required fields are marked *
Comment *
Name *
Email *
Post Comment
Writen by:
Kostis Kapelonis
Kostis is a Software Engineer who loves clean and
compact code, simple solutions and modular distributed
systems. He hates feature creep, over-engineering, XML
gluttony and monolithic systems. He has a soft spot for
code Quality and build systems.
CI/CD Weekly
Newsletter
Tutorials, interviews, and tips for you to become a well-rounded developer.
Email Subscribe
Resources
Blog
Podcast
Guides
Newsletter
Write with us
Product
Features
Pricing
Docs
Customers
Premium Support
System Status
Security
© 2024 Rendered Text. All rights reserved. Terms of Service Privacy Policy