Reputation: 11389
All,
Thanks for help. I am pretty new to Mockito, If I have a Service class, a controller class(which using that serivce by passing in a Map param), How can I mock that service method?
Helloworldservice.java
package service;
import java.util.Map;
public class Helloworldservice {
public String greeting() {
return "Hello, World";
}
public String greetingSB(Map<String, String> sb) {
return "Hello," + sb.get("name");
}
}
Helloworldcontroller.java
package controller;
import java.util.HashMap;
import java.util.Map;
import service.Helloworldservice;
public class Helloworldcontroller {
private Helloworldservice hservice;
public Helloworldcontroller() {
// TODO Auto-generated constructor stub
hservice = new Helloworldservice();
}
public String sayHello() {
return hservice.greeting();
}
public String sayHelloSB() {
Map<String, String> sb = new HashMap<String, String>();
sb.put("name", "somebody");
return hservice.greetingSB(sb);
}
}
HelloworldcontrollerTest.java
package unit.controller;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import controller.Helloworldcontroller;
import service.Helloworldservice;
@RunWith(MockitoJUnitRunner.class)
public class HelloworldcontrollerTest {
@InjectMocks
private Helloworldcontroller hcontroller;
private Helloworldservice hservice = new Helloworldservice();
@Mock
private Helloworldservice hservice_mock;
@Before
public void setup() {
hservice_mock = Mockito.spy(hservice);
/** I am not sure how to mock here for that param sb
Mockito.when(hservice_mock.greetingSB(.......))
.thenReturn("Hello, somebody");
**/
}
@Test
public void testGreeting() {
String h = hcontroller.sayHelloSB();
Assert.assertEquals(h, "Hello, sombody!!!");
}
}
The serice always returns null, I am not sure what is wrong.
Upvotes: 4
Views: 7018
Reputation: 373
There're several places need to be fixed in you test class, as listed in number 1, 2 ...
@RunWith(MockitoJUnitRunner.class)
public class HelloworldcontrollerTest {
@InjectMocks
private Helloworldcontroller hcontroller;
private Helloworldservice hservice = new Helloworldservice();
@Mock
private Helloworldservice hservice_mock;
@Before
public void setup() {
// 1. Comment out this line, because you've already created a mock instance using @Mock
// hservice_mock = Mockito.spy(hservice);
/** I am not sure how to mock here for that param sb
Mockito.when(hservice_mock.greetingSB(.......))
.thenReturn("Hello, somebody");
**/
}
@Test
public void testGreeting() {
// 2. This's why program output null. If you create an entire mock(not a real mock or a partial mock) object, then you should give the specific expectation for the method.
Mockito.when(hservice_mock.greetingSB(Mockito.any())).thenReturn("Hello world!!");
String h = hcontroller.sayHelloSB();
Assert.assertEquals(h, "Hello, sombody!!!");
}
}
For the difference between entire mock and partial mock, you can refer to this link.
In summary, the basic steps to use mock is:
Upvotes: 1
Reputation: 6780
Your sample code is a bit off as it'll work without any mocking involved.
public class HelloworldcontrollerTest {
private Helloworldcontroller hcontroller = new Helloworldcontroller();
@Test
public void testGreeting() {
String h = hcontroller.sayHelloSB();
Assert.assertEquals(h, "Hello,somebody");
}
}
Anyway, it's probably just that, a sample.
The problem with mocking lies with this line
hservice_mock = Mockito.spy(hservice);
First, you let Mockito create your mock (@Mock Helloworldservice hservice_mock
) and inject it into the controller (@InjectMocks Helloworldcontroller hcontroller
) and then you're creating a spy on your own (hservice_mock = Mockito.spy(hservice)
) for which you try to setup your expectations (when(hservice_mock.greetingSB(...))
). Setting up expectations on spies require a different method invocation chain, hence a NullPointerException
would occur currently (see Important gotcha on spying real objects!). Even if it would work, it wouldn't affect the already injected mock.
This would work as expected:
@RunWith(MockitoJUnitRunner.class)
public class HelloworldcontrollerTest {
@InjectMocks
private Helloworldcontroller hcontroller = new Helloworldcontroller();
@Mock
private Helloworldservice hservice_mock;
@Before
public void setup() {
Mockito.when(hservice_mock.greetingSB(any(Map.class)))
.thenReturn("Hello, somebody!!!");
}
@Test
public void testGreeting() {
String h = hcontroller.sayHelloSB();
Assert.assertEquals(h, "Hello, somebody!!!");
}
}
A few other comments on the test setup:
Helloworldcontroller
has dependency to Helloworldservice
. Instead of creating the instance in the constructor, you should consider using constructor dependency injection. Your sample code works but things go out of control should it become more complex. Think of database access.@InjectMocks
is a sign of code smell and indicates a deeper problem with the code to be tested. Try to avoid if possible.assertEquals
as in assertEquals(h, "Hello, sombody!!!")
is the expected value, then comes the actual value. Sounds not very important, but affects assertion violation error message. Let's tackle those problems and see how we can improve the code.
First, use constructor injection.
public class Helloworldcontroller {
private final Helloworldservice hservice;
public Helloworldcontroller(Helloworldservice hservice) {
this.hservice = hservice;
}
public String sayHello() {
return hservice.greeting();
}
public String sayHelloSB() {
Map<String, String> sb = new HashMap<String, String>();
sb.put("name", "somebody");
return hservice.greetingSB(sb);
}
}
The test code becomes now
@RunWith(MockitoJUnitRunner.class)
public class HelloworldcontrollerTest {
private Helloworldcontroller hcontroller;
@Mock
private Helloworldservice hservice;
@Before
public void setup() {
hcontroller = new Helloworldcontroller(hservice);
Mockito.when(hservice.greetingSB(any(Map.class)))
.thenReturn("Hello, somebody!!!");
}
@Test
public void testGreeting() {
Assert.assertEquals("Hello, somebody!!!", hcontroller.sayHelloSB());
}
}
Upvotes: 3