Mockito Hello World Example
You are here, it means either you are interested in the mock objects or you are already aware of Mockito, and you want to see a working example of it. Well…let me first introduce you to Mockito which is an open source mock unit testing framework for Java. In this article, I am going to show you a Hello World example of Mockito. You will learn how to use it for mock object creation, stubbing and verification. I’ll also explain in detail how mock objects work, how they encourage testing based on behavior verification.
My setup consists of :
In case you are new to TestNG, TestNG Maven Project Example will guide you on how to setup a Maven based project and run the TestNG tests.
We will start with a hello world example but first lets add Mockito dependency to our pom.xml
.
1. Mockito Dependency
Add mockito-core
to 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>com.javacodegeeks.testng.maven</groupId> <artifactId>testngMaven</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.8.8</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.0.5-beta</version> </dependency> </dependencies> </project>
2. Mockito Hello World Example
I will be using Foo
and Bar
to demonstrate my example.
Let’s bring Foo
into picture and make it greet us.
Foo:
package com.javacodegeeks.mockito; public interface Foo { String HELLO_WORLD = "Hello World"; String greet(); }
Note that Foo
is just an interface. Let’s imagine that the implementation would be provided by some other team which is either still working on it or is not yet done with the implementation. Now assuming that a portion of your component is dependent on Foo
‘s API, the idea of you waiting for the delivery of Foo
is not so encouraging. This is where we will have to switch hats and apply a mockist approach to our problem.
2.1. Building Mock Object
Let’s write our first test that will allow us to peep into Mockito’s world.
Our first scenario would be to call foo.greet()
and make it return “Hello World”. This will introduce us to concepts like mocking and training the mock object.
Our test consists of:
- Creating a mock Foo object
- and then train it to return us “Hello World” when
Foo.greet()
is called. This will set up the expectations that we have from the mock object.
MockitoHelloWorldExample:
package com.javacodegeeks.mockito; import static com.javacodegeeks.mockito.Foo.*; import static org.mockito.Mockito.*; import static org.testng.Assert.*; import org.testng.annotations.Test; public class MockitoHelloWorldExample { @Test public void fooGreets() { Foo foo = mock(Foo.class); when(foo.greet()).thenReturn(HELLO_WORLD); System.out.println("Foo greets: " + foo.greet()); assertEquals(foo.greet(), HELLO_WORLD); } }
Output:
Foo greets: Hello World PASSED: fooGreets =============================================== Default test Tests run: 1, Failures: 0, Skips: 0 ===============================================
2.2. Mockito APIs Used
Let’s now review the Mockito APIs that we have called.
We have used Mockito.mock(Foo.class)
to create the mock object. Since will be calling Mockito APIs quite often, we can improve the clarity of API call by statically importing the package org.mockito.Mockito.*
. We don’t have to make the explicit static calls any more. If you notice in our above test, to create the mock object, I have simply called mock(Foo.class)
.
The next thing we do is to setup our expectations. Our expectation is, when foo.greet()
is called then return ‘Hello World’. The API construction is designed to be more readable and English like. To achieve it, we call:
when(foo.greet()).thenReturn(HELLO_WORLD)
The API follows builder pattern where each method returns us an Object of type OngoingStubbing
so that we can stub further on the returned object thus allowing us to build the expectations fluently.
2.3. System Under Test
Ok, this works but that’s not our goal. Foo
is only a collaborator and not the system under test, also fondly called, SUT. Let’s bring our SUT Bar
into the field.
Bar
has a method called greet(Foo)
which takes in a Foo object, makes a call to foo.greet()
and returns us Foo
‘s greeting.
Bar:
package com.javacodegeeks.mockito; public class Bar { public String greet(Foo foo) { System.out.println("Bar invokes Foo.greet"); return foo.greet(); } }
We will now add our new test barGreets()
which just makes sure that Foo returns us the proper response. Since even our second test makes use of the mock object, we have moved the setting up of mock object to our new configuration method setupMock(),
which is a @BeforeMethod
that gets called right before the invocation of each test method.
MockitoHelloWorldExample:
package com.javacodegeeks.mockito; import static com.javacodegeeks.mockito.Foo.*; import static org.mockito.Mockito.*; import static org.testng.Assert.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class MockitoHelloWorldExample { private Foo foo; @BeforeMethod public void setupMock() { foo = mock(Foo.class); when(foo.greet()).thenReturn(HELLO_WORLD); } @Test public void fooGreets() { System.out.println("Foo greets: " + foo.greet()); assertEquals(HELLO_WORLD, foo.greet()); } @Test public void barGreets() { Bar bar = new Bar(); assertEquals(HELLO_WORLD, bar.greet(foo)); } }
Output:
Bar invokes Foo.greet PASSED: barGreets =============================================== Default test Tests run: 1, Failures: 0, Skips: 0 ===============================================
Ok, that looks good. We have a SUT and a collaborator. We are able to test SUT even though the actual collaborator implementation is not yet available. Thanks to the Mock object!.
If you notice, Bar
is way too simplistic. Let’s spice it up and add a few conditions.
3. Behavior Verification
We will now add a new method to Bar
called question(Foo foo, String question)
which takes in a question, sends it to Foo
and then returns us Foo
‘s answer. As promised, we will spice it up a bit with a few conditions:
- First, we need to make sure
Foo
is available. We will know it is available whenfoo.greet()
returns us “Hello World”. - If
Foo
is unavailable,Bar
will not questionFoo
any more and instead will throwFooNotAvailable
exception. Foo
will answer only certain questions..- Any other question sent,
Bar
will simply filter it out, without requestingFoo
for an answer and instead will return “Invalid Question”.
Bar:
package com.javacodegeeks.mockito; public class Bar { public String greet(Foo foo) { System.out.println("Bar invokes Foo.greet"); return foo.greet(); } public String question(Foo foo, String question) { verifyFooConnection(foo); if (Foo.ANY_NEW_TOPICS.equals(question)) { return foo.question(question); } return "Invalid Question"; } public void verifyFooConnection(Foo foo) { System.out.println("Is Foo available?"); String response = foo.greet(); if (!Foo.HELLO_WORLD.equals(response)) { System.out.println("No"); throw new FooNotAvailable(); } System.out.println("Yes"); } }
Foo:
package com.javacodegeeks.mockito; public interface Foo { String HELLO_WORLD = "Hello World"; String ANY_NEW_TOPICS = "Are there any new topics?"; String greet(); String question(String question); }
FooNotAvailable:
package com.javacodegeeks.mockito; public class FooNotAvailable extends RuntimeException { }
Now let’s add few tests and see how our SUT responds.
MockitoHelloWorldExample:
package com.javacodegeeks.mockito; import static com.javacodegeeks.mockito.Foo.*; import static org.mockito.Mockito*; import static org.testng.Assert.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class MockitoHelloWorldExample { private Foo foo; @BeforeMethod public void setupMock() { foo = mock(Foo.class); when(foo.greet()).thenReturn(HELLO_WORLD); } @Test public void fooGreets() { System.out.println("Foo greets: " + foo.greet()); assertEquals(HELLO_WORLD, foo.greet()); } @Test public void barGreets() { Bar bar = new Bar(); assertEquals(HELLO_WORLD, bar.greet(foo)); } @Test(expectedExceptions=FooNotAvailable.class) public void fooNotAvailable() { Bar bar = new Bar(); System.out.println("Foo is down so will not respond"); when(foo.greet()).thenReturn(null); System.out.println("Bar sends a question to Foo but since Foo is not avilable will throw FooNotAvailable"); bar.question(foo, "Hello Foo"); } @Test public void barQuestionsFoo() { Bar bar = new Bar(); System.out.println("Bar asks Foo 'Any new topics?', it should get a response"); bar.question(foo, Foo.ANY_NEW_TOPICS); System.out.println("Verify that Foo has been asked the question"); verify(foo, times(1)).question(Foo.ANY_NEW_TOPICS); } @Test public void filterInvalidQuestions() { Bar bar = new Bar(); String invalidQuestion = "Invalid question"; bar.question(foo, invalidQuestion); System.out.println("Verify that question was never requested as Foo is un-available"); verify(foo, never()).question(invalidQuestion); } }
Output:
Foo is down so will not respond Bar sends a question to Foo but since Foo is not avilable will throw FooNotAvailable Is Foo available? No PASSED: fooNotAvailable Bar asks Foo 'Any new topics?', it should get a response Is Foo available? Yes Verify that Foo has been asked the question PASSED: barQuestionsFoo Is Foo available? Yes Verify that question was never requested as Foo is unavailable PASSED: filterInvalidQuestions
Let’s review each test case.
fooNotAvailable()
tests whetherBar.question
throwsFooNotAvailable
exception whenFoo
is unavailable. We train the mock objectFoo
to return null whengreet()
is called.@Test
attributeexpectedExceptions
asserts our expected exception.barQuestionsFoo
sends a valid question to theBar
and then verifies whether it has been delegated toFoo.question
for an answer.filterInvalidQuestions
sends an invalid question to theBar
and then verifies thatFoo.question
has not been called.
4. Mock Object throwing Exceptions
Till now, it was Bar
, deciding whether a question is valid. Let’s push this responsibility to Foo.
This makes sense as it is Foo
which has to decide whether to answer or not. Since Foo
now knows which one is valid and which is not, in case of an invalid question, it will reject the question and throw an InvalidQuestion
exception. With this change, our Foo
interface looks like below.
Foo:
package com.javacodegeeks.mockito; public interface Foo { String HELLO_WORLD = "Hello World"; String ANY_NEW_TOPICS = "Are there any new topics?"; String greet(); String question(String question); String questionStrictly(String question) throws InvalidQuestion; }
Few points to note about Foo
.
- We have added a new method
questionStrictly(question)
which strictly verifies whether a question is valid. - In case of an invalid question, it is expected to throw
InvalidQuestion
. - Else it is supposed to return an answer.
Bar:
package com.javacodegeeks.mockito; public class Bar { public String greet(Foo foo) { System.out.println("Bar invokes Foo.greet"); return foo.greet(); } public String question(Foo foo, String question) { verifyFooConnection(foo); if (Foo.ANY_NEW_TOPICS.equals(question)) { return foo.question(question); } return "Invalid Question"; } public String questionStrictly(Foo foo, String question) throws InvalidQuestion { verifyFooConnection(foo); String answer= foo.questionStrictly(question); return answer; } public void verifyFooConnection(Foo foo) { System.out.println("Is Foo available?"); String response = foo.greet(); if (!Foo.HELLO_WORLD.equals(response)) { System.out.println("No"); throw new FooNotAvailable(); } System.out.println("Yes"); } }
Let’s now add a test case throwExceptionIfInvalidQuestion
to assert whether Foo
will reject an invalid question.
MockitoHelloWorldExample:
package com.javacodegeeks.mockito; import static com.javacodegeeks.mockito.Foo.*; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.*; import static org.testng.Assert.*; import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class MockitoHelloWorldExample { private Foo foo; private final static ValidQuestions VALID_QUESTIONS = new ValidQuestions(); @BeforeMethod public void setupMock() { foo = mock(Foo.class); when(foo.greet()).thenReturn(HELLO_WORLD); } @Test public void fooGreets() { System.out.println("Foo greets: " + foo.greet()); assertEquals(HELLO_WORLD, foo.greet()); } @Test public void barGreets() { Bar bar = new Bar(); assertEquals(HELLO_WORLD, bar.greet(foo)); } @Test(expectedExceptions=FooNotAvailable.class) public void fooNotAvailable() { Bar bar = new Bar(); System.out.println("Foo is down so will not respond"); when(foo.greet()).thenReturn(null); System.out.println("Bar sends a question to Foo but since Foo is not avilable will throw FooNotAvailable"); bar.question(foo, "Hello Foo"); } @Test public void barQuestionsFoo() { Bar bar = new Bar(); System.out.println("Bar asks Foo 'Any new topics?', it should get a response"); bar.question(foo, Foo.ANY_NEW_TOPICS); System.out.println("Verify that Foo has been asked the question"); verify(foo, times(1)).question(Foo.ANY_NEW_TOPICS); } @Test public void filterInvalidQuestions() { Bar bar = new Bar(); String invalidQuestion = "Invalid question"; bar.question(foo, invalidQuestion); System.out.println("Verify that question was never requested as Foo is un-available"); verify(foo, never()).question(invalidQuestion); } @Test(expectedExceptions=InvalidQuestion.class) public void throwExceptionIfInvalidQuestion() throws InvalidQuestion { Bar bar = new Bar(); String invalidQuestion = "Invalid question"; when(foo.questionStrictly("Invalid question")).thenThrow(new InvalidQuestion()); bar.questionStrictly(foo, invalidQuestion); } }
Our new scenario is, when foo.questionStrictly()
is passed an invalid question, Foo
should throw InvalidQuestion
. The exception expected is setup using thenThrow()
API which accepts the exception to be thrown. After the setup, bar.questionStrictly()
is called with the invalid question. Our expectedExceptions
test attribute makes sure that the exception is thrown.
when(foo.questionStrictly("Invalid question")).thenThrow(new InvalidQuestion());
5. More Behavior Verification using ArgumentMatcher and Answer Callback
We will further modify our Bar
class so that now it can respond to the answers received from Foo
. Based on the answer received, it will make further calls to Foo
.
Bar
will ask Foo
, whether there are any new topics of discussion. For example, a tutorial topic. Foo
may either answer in affirmative or negative. If they are no new topics, Bar
will call foo.bye()
to indicate the end of discussion.
If there are new topics, Bar
will further ask Foo
the current day’s topic and its price. Once it receives the price, it will call foo.bye()
to end the session.
Let’s see our modified Bar
class.
Bar:
package com.javacodegeeks.mockito; public class Bar { public String greet(Foo foo) { System.out.println("Bar invokes Foo.greet"); return foo.greet(); } public String question(Foo foo, String question) { verifyFooConnection(foo); if (Foo.ANY_NEW_TOPICS.equals(question)) { return foo.question(question); } return "Invalid Question"; } public String questionStrictly(Foo foo, String question) throws InvalidQuestion { verifyFooConnection(foo); System.out.println(question); String answer= foo.questionStrictly(question); switch (answer) { case Foo.NO_NEW_TOPIC: System.out.println("No"); System.out.println("Let's quit now"); foo.bye(); break; case Foo.YES_NEW_TOPICS_AVAILABLE: System.out.println("Yes"); System.out.println(Foo.WHAT_IS_TODAYS_TOPIC); answer = foo.questionStrictly(Foo.WHAT_IS_TODAYS_TOPIC); System.out.println("Topic name is " + answer); System.out.println("What is the price?"); int price = foo.getPrice(answer); System.out.println("Price is " + price); System.out.println("Let's quit now"); foo.bye(); answer = "Topic is " + answer + ", price is " + price; break; default: System.out.println("Answer is " + answer); break; } return answer; } public void verifyFooConnection(Foo foo) { System.out.println("Is Foo available?"); String response = foo.greet(); if (!Foo.HELLO_WORLD.equals(response)) { System.out.println("No"); throw new FooNotAvailable(); } System.out.println("Yes"); } }
New additions to Foo
are the APIs getPrice(tutorial)
and bye()
and a few constants.
Foo:
package com.javacodegeeks.mockito; public interface Foo { String HELLO_WORLD = "Hello World"; String ANY_NEW_TOPICS = "Are there any new topics?"; String WHAT_IS_TODAYS_TOPIC = "What is todays topic?"; String YES_NEW_TOPICS_AVAILABLE = "Yes"; String NO_NEW_TOPIC = "No"; String TOPIC_MOCKITO = "Mockito"; String greet(); int getPrice(String tutorial); String question(String question); String questionStrictly(String question) throws InvalidQuestion; void bye(); }
In our previous test case throwExceptionIfInvalidQuestion
, we had explicitly checked for “Invalid Question” but note that there can be more questions which fall into the invalid zone. Also, since Bar
now responds to answers, we need to setup our mock object to map the questions and answers.
MockitoHelloWorldExample:
package com.javacodegeeks.mockito; import static com.javacodegeeks.mockito.Foo.*; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.*; import static org.testng.Assert.*; import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class MockitoHelloWorldExample { private Foo foo; private final static ValidQuestions VALID_QUESTIONS = new ValidQuestions(); @BeforeMethod public void setupMock() { foo = mock(Foo.class); when(foo.greet()).thenReturn(HELLO_WORLD); } @Test public void fooGreets() { System.out.println("Foo greets: " + foo.greet()); assertEquals(HELLO_WORLD, foo.greet()); } @Test public void barGreets() { Bar bar = new Bar(); assertEquals(HELLO_WORLD, bar.greet(foo)); } @Test(expectedExceptions = FooNotAvailable.class) public void fooNotAvailable() { Bar bar = new Bar(); System.out.println("Foo is down so will not respond"); when(foo.greet()).thenReturn(null); System.out .println("Bar sends a question to Foo but since Foo is not avilable will throw FooNotAvailable"); bar.question(foo, "Hello Foo"); } @Test public void barQuestionsFoo() { Bar bar = new Bar(); System.out .println("Bar asks Foo 'Any new topics?', it should get a response"); bar.question(foo, Foo.ANY_NEW_TOPICS); System.out.println("Verify that Foo has been asked the question"); verify(foo, times(1)).question(Foo.ANY_NEW_TOPICS); } @Test public void filterInvalidQuestions() { Bar bar = new Bar(); String invalidQuestion = "Invalid question"; bar.question(foo, invalidQuestion); System.out .println("Verify that question was never requested as Foo is un-available"); verify(foo, never()).question(invalidQuestion); } @Test(expectedExceptions = InvalidQuestion.class) public void throwExceptionIfInvalidQuestion() throws InvalidQuestion { Bar bar = new Bar(); String invalidQuestion = "Invalid question"; when(foo.questionStrictly("Invalid question")).thenThrow( new InvalidQuestion()); bar.questionStrictly(foo, invalidQuestion); } @Test(expectedExceptions = InvalidQuestion.class) public void throwExceptionIfAnyInvalidQuestion() throws InvalidQuestion { Bar bar = new Bar(); String invalidQuestion = "Invalid question"; when(foo.questionStrictly(argThat(new InValidQuestions()))).thenThrow( new InvalidQuestion()); bar.questionStrictly(foo, invalidQuestion); } @Test public void getTodaysTopicPrice() throws InvalidQuestion { Bar bar = new Bar(); when(foo.questionStrictly(argThat(new ValidQuestions()))).thenAnswer( new FooAnswers()); when(foo.getPrice(TOPIC_MOCKITO)).thenReturn(20); String answer = bar.questionStrictly(foo, ANY_NEW_TOPICS); System.out.println("Answer is: " + answer); assertEquals(answer, "Topic is Mockito, price is 20"); verify(foo, times(1)).questionStrictly(WHAT_IS_TODAYS_TOPIC); verify(foo, times(1)).getPrice(TOPIC_MOCKITO); verify(foo, times(1)).bye(); } @Test public void noNewTopic() throws InvalidQuestion { Bar bar = new Bar(); when(foo.questionStrictly(ANY_NEW_TOPICS)).thenReturn(NO_NEW_TOPIC); String answer = bar.questionStrictly(foo, ANY_NEW_TOPICS); System.out.println("Answer is: " + answer); assertEquals(answer, NO_NEW_TOPIC); verify(foo, times(1)).bye(); verify(foo, never()).questionStrictly(WHAT_IS_TODAYS_TOPIC); verify(foo, never()).getPrice(TOPIC_MOCKITO); } private static class InValidQuestions extends ArgumentMatcher { @Override public boolean matches(Object argument) { return !VALID_QUESTIONS.matches(argument); } } private static class ValidQuestions extends ArgumentMatcher { @Override public boolean matches(Object argument) { return argument.equals(ANY_NEW_TOPICS) || argument.equals(WHAT_IS_TODAYS_TOPIC); } } private static class FooAnswers implements Answer { public String answer(InvocationOnMock invocation) throws Throwable { String arg = (String) invocation.getArguments()[0]; if (ANY_NEW_TOPICS.equals(arg)) { return YES_NEW_TOPICS_AVAILABLE; } else if (WHAT_IS_TODAYS_TOPIC.equals(arg)) { return TOPIC_MOCKITO; } else { throw new InvalidQuestion(); } } } }
Output:
Is Foo available? Yes Invalid question PASSED: throwExceptionIfAnyInvalidQuestion Is Foo available? Yes Are there any new topics? Yes What is todays topic? Topic name is Mockito What is the price? Price is 20 Let's quit now Answer is: Topic is Mockito, price is 20 PASSED: getTodaysTopicPrice Is Foo available? Yes Are there any new topics? No Let's quit now Answer is: No PASSED: noNewTopic
Mock objects return expected values. But when it needs to return different values for different arguments, Mockito’s argument matcher comes into play. In our case, the system has to behave in one way if the questions asked are valid ones and in a different way if they are ‘invalid’ which the collaborator doesn’t know how to respond.
Let’s go through our new test cases:
throwExceptionIfAnyInvalidQuestion
– instead of testing the code against one invalid value, it now tests on a sub-set of values using theArgumentMatcher
when(foo.questionStrictly(argThat(new InValidQuestions()))).thenThrow(new InvalidQuestion());
We pass in a
object toorg.mockito.ArgumentMatcher
argThat(),
so that the argument passed in tofoo.questionStrictly()
can be tested against the matcher to know whether it is one of the arguments expected by the mock object. If yes, then the next stub action will follow, in our case, the method will throw anInvalidQuestion
exception, if the argument value is not a valid question.getTodaysTopicPrice
– here ourBar
asksFoo
whether there are new tutorials. The question asked is one of the valid ones soFoo
responds with the current topic.Bar
then asks for the price of the latest tutorial. Finally,Bar
requestsFoo
to end the session callingfoo.bye()
. We setup our expectations on the mock object using the below statements.when(foo.questionStrictly(argThat(new ValidQuestions()))).thenAnswer( new FooAnswers()); when(foo.getPrice(TOPIC_MOCKITO)).thenReturn(20);
After setting up, we call on the actual SUT method to start the test.
bar.questionStrictly(foo, ANY_NEW_TOPICS)
After this, we do the verification to make sure that
questionStrictly
has interacted withFoo
the way we wanted.
OnceFoo
responds that there are new topics,Bar
asksFoo
further details about the topic and then finally exits.
We do the verification of the calls made to Foo below:verify(foo, times(1)).questionStrictly(WHAT_IS_TODAYS_TOPIC); verify(foo, times(1)).getPrice(TOPIC_MOCKITO); verify(foo, times(1)).bye();
noNewTopic
– hereFoo
returns response with no new topics in which case,Bar
calls onfoo.bye()
to exit the communication
Download the Eclipse project
This was a Mockito Hello World Example.
You can download the full source code of this example here: mockitoHelloWorld
Thanks for sharing
It’s really Fantastic Article. I like that kind of Information and Tips. I would be recommend That’s knowledge and share my social channels. It’s really good one. Keep it up.
https://www.protegent360.com/protegent-complete-security.html
Keep it up hard work.