2

I have a Interface which is registered as part of ServiceLocatorFactoryBean. The main purpose of this Interface is to act as a factory.

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html

I have "autowired" this Interface in various classes, that I want to test now with Mockito.

The issue is Mockito doesn't support interfaces. How can inject a mock of this interface in the class I am testing.

The only alternative I see is to run the test using SpringJunitRunner and providing an Application Context which has the bean configurations. But this is too verbose.

manoj84
  • 37
  • 1
  • 8
  • What do you mean by "Mockito does not support interfaces"? It totally does :) What is the issue with this for example: `MyInterface myInterface = Mockito.mock(MyInterface.class);` You can then set this when you create the class you want to test. – Kotse Feb 18 '16 at 07:18
  • A small correction in the above question, I am trying to @spy on the interface rather than mock it. The issue is that I do not have concrete implementation of the class for that interface. if you look at the ServiceLocatorFactoryBean link I have posted above, the documentation mentions that you just create an interface and let it be, and spring provides run time implementation of the interface. But, spy cannot work with interface. This is from Mockito.spy documentation " But Mockito cannot instantiate inner classes, local classes, abstract classes and interfaces." – manoj84 Feb 18 '16 at 17:39

1 Answers1

0

I take it you'd like to spy on the implementation that Spring generated for your interface?! That's close to impossible to achieve with what you have so far... However there are at least the following alternatives below.

Suppose we have the following setup:

public interface MyService {
    String doIt();
}
@Component
public static class ServiceConsumer {
    @Autowired
    private MyService service;

    public String execute() {
        return service.doIt();
    }
}

0) Later edit: while roaming around, I found that it may be possible to spy and even replace an autowired field with a mock, and fairly easy too, using Springockito-annotations.

@RunWith(SpringJUnit4ClassRunner.class)
@ComponentScan
@ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class, classes = {SpringockitoConsumerTest.class})
public class SpringockitoConsumerTest {

    @WrapWithSpy(beanName = "myService")
    @Autowired
    private MyService mockService;

    @Autowired
    private ServiceConsumer consumer;

    @Test
    public void shouldConsumeService() {
        assertEquals("allDone", consumer.execute());
        verify(mockService).doIt();
    }
}

If Springockito-annotations is out of the question, please see the 2 original suggestions below


1) You could just create your mock of the interface and auto-inject it Mockito in your bean. This is the simplest solution (I could think of at the time of writing) but it does not ensure that the @Autowired annotation was not forgotten in the consumer (perhaps a dedicated test could be added?!):

public class AutoInjectMocksConsumerTest {

    @Mock
    private MyService serviceMock;

    @InjectMocks
    private ServiceConsumer consumer = new ServiceConsumer();

    @Before
    public void initMocks() {
        MockitoAnnotations.initMocks(this);
        when(serviceMock.doIt()).thenReturn("allDone");
    }

    @Test
    public void shouldConsumeService() {
        assertEquals("allDone", consumer.execute());
        verify(serviceMock).doIt();
    }
}

2) Alternatively as you also said, you could run it with the SpringJunitRunner making a minimum of effort to define and instantiate the necessary Spring context while also providing your own service mock. Albeit people may complain this solution is not that clean, I find it sufficiently elegant and it also validates that the @Autowired annotation was not forgotten in the consumer implementation.

@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
@ComponentScan
@ContextConfiguration(classes = {SpringAutowiringConsumerTest.class})
public class SpringAutowiringConsumerTest {

    @Autowired
    private MyService mockService;

    @Autowired
    private ServiceConsumer consumer;

    @Test
    public void shouldConsumeService() {
        assertEquals("allDone", consumer.execute());
        verify(mockService).doIt();
    }

    @Bean
    public MyService mockService() {
        MyService serviceMock = mock(MyService.class);
        when(serviceMock.doIt()).thenReturn("allDone");
        return serviceMock;
    }
}
Community
  • 1
  • 1
Morfic
  • 15,178
  • 3
  • 51
  • 61