12

I'm facing problems mocking services injected inside of other services within the Spring framework. Here is my code:

@Service("productService")
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ClientService clientService;

    public void doSomething(Long clientId) {
        Client client = clientService.getById(clientId);
        // do something
    }
}

I want to mock the ClientService inside my test, so I tried the following:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/spring-config.xml" })
public class ProductServiceTest {

    @Autowired
    private ProductService productService;

    @Mock
    private ClientService clientService;

    @Test
    public void testDoSomething() throws Exception {
        when(clientService.getById(anyLong()))
                .thenReturn(this.generateClient());

        /* when I call this method, I want the clientService
         * inside productService to be the mock that one I mocked
         * in this test, but instead, it is injecting the Spring 
         * proxy version of clientService, not my mock.. :(
         */
        productService.doSomething(new Long(1));
    }

    @Before
    public void beforeTests() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    private Client generateClient() {
        Client client = new Client();
        client.setName("Foo");
        return client;
    }
}

The clientService inside productService is the Spring proxy version, not the mock that I want. Is it possible to do what I want with Mockito?

aschipfl
  • 33,626
  • 12
  • 54
  • 99
br4zuca
  • 121
  • 1
  • 1
  • 4

4 Answers4

6

You need to annotate ProductService with @InjectMocks:

@Autowired
@InjectMocks
private ProductService productService;

This will inject the ClientService mock into your ProductService.

Debojit Saikia
  • 10,532
  • 3
  • 35
  • 46
  • Thanks Debojit, but I realized there's no way to use the spring's injections in combination with mockito's mock injections... so my solution was to create the ProductService by hands and inject the mocks with `@InjectMocks` like you said, but not using `@Autowired`: `@InjectMocks private ProductService productService = new ProductServiceImpl();` – br4zuca Sep 25 '13 at 19:17
  • That's strange. I have been using these two annotations together. Have you tested it. Is there any exception while using these two together. – Debojit Saikia Sep 26 '13 at 02:52
  • Yes, I tested it a lot, even with the `@InjectMocks` on ProductService the clientService inside was load with the spring proxy instead the mocked object. Maybe the spring-test version you are using differ from that I'm using, my version is 3.0.1.RELEASE, what's your version? – br4zuca Sep 26 '13 at 13:03
  • i am using 3.1.2.RELEASE with mockito 1.9.5 – Debojit Saikia Sep 26 '13 at 13:07
  • I changed to 3.1.2-RELEASE and got same results... maybe your config differ from mine in some point, well, thanks anyway, for now I'll use the manual instantiation approach. – br4zuca Sep 26 '13 at 14:58
  • what if clientService has again repository autowired (productService > clientService > xyzRepository) & we need to mock the repo (xyzRepository), what needs to be done to mock? – Satish Patro Oct 08 '21 at 05:58
1

There are more ways to achieve this, the most easy way to do this will be don't use field injection, but setter injection which means you should have:

@Autowired
public void setClientService(ClientService clientService){...}

in your service class, then you can inject your mock to the service in test class:

@Before
public void setUp() throws Exception {
    productService.setClientService(mock);
}

important: If this is only a unit test, please consider don't use SpringJUnit4ClassRunner.class, but MockitoJunitRunner.class, so that you can also use field inject for your fields.

Jaiwo99
  • 9,687
  • 3
  • 36
  • 53
  • 1
    Thanks, I tried that solution and worked fine, but I cannot change to setter injection in that services, so my solution was to remove the autowired annotation on my tests and create the service by hands, like following: `@InjectMocks private ProductService productService = new ProductServiceImpl();` – br4zuca Sep 25 '13 at 19:21
  • @br4zuca you can do it with `@InjectMocks private ProductService productService`, don't init it manual, let mockito do it. – Jaiwo99 Sep 25 '13 at 20:55
  • 1
    I tried leave mockito init the `productService` with the `@InjectMocks`, but didn't work because `ProductService` is an interface, it throwed: `org.mockito.exceptions.base.MockitoException: the type 'ProductService' is an interface`, so it was not able to instantiate. – br4zuca Sep 26 '13 at 13:02
  • 2
    I found a solution for that, instead a declare `@InjectMock ProductService productService`, I changed the `ProductService` interface to impl like `ProductServiceImpl`, then in that form the mockito was able to auto init my service, like: `@InjectMock ProductServiceImpl productService` – br4zuca Sep 26 '13 at 15:03
  • what if clientService has again repository autowired (productService > clientService > xyzRepository) & we need to mock the repo (xyzRepository), what needs to be done to mock? – Satish Patro Oct 08 '21 at 05:59
1

In addition to

@Autowired
@InjectMocks
private ProductService productService;

Add the following method

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}
Jacky1205
  • 3,273
  • 3
  • 22
  • 44
  • what if clientService has again repository autowired (productService > clientService > xyzRepository) & we need to mock the repo (xyzRepository), what needs to be done to mock? – Satish Patro Oct 08 '21 at 05:58
1

I would like to suggest you annotate the Test target with @InjectMock

Currently

    @Autowired
    private ProductService productService;

    @Mock
    private ClientService clientService;

Change to

    @InjectMock
    private ProductService productService;

    @Mock
    private ClientService clientService;

incase you still have NullPointerException for the MockingService => you can use Mockito.any() as arguments. Hopefully, it will help you.

infD
  • 43
  • 7
  • what if clientService has again repository autowired (productService > clientService > xyzRepository) & we need to mock the repo (xyzRepository), what needs to be done to mock? – Satish Patro Oct 08 '21 at 05:58
  • @SatishPatro, I've to implement something similar. Did you figure a way out? – tez Mar 20 '23 at 04:43
  • @tez we need to do step by step. If controller call service. And, service call repo. In controller test class, you mock service. In Service test class you mock repo. It's like each class will have it's own test class – Satish Patro Mar 20 '23 at 08:35