使用 ServiceLocatorFactoryBean Autowired 对 class 进行单元测试

Unit Testing a class with ServiceLocatorFactoryBean Autowired

我有一个注册为 ServiceLocatorFactoryBean 一部分的接口。这个接口的主要目的是作为一个工厂。

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

我在各种 class 中有 "autowired" 这个接口,我现在想用 Mockito 测试它。

问题是 Mockito 不支持接口。如何在我正在测试的 class 中注入此接口的模拟。

我看到的唯一选择是 运行 使用 SpringJunitRunner 并提供具有 bean 配置的应用程序上下文的测试。但这太冗长了。

我猜你想监视 Spring 为你的界面生成的实现?!到目前为止,这几乎是不可能实现的……但是至少有以下替代方案。

假设我们有以下设置:

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

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

0) 稍后编辑: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();
    }
}

如果Springockito-annotations不对,请看下面2条原创建议


1) 您可以只创建您的接口模拟并将其自动注入您的 bean 中的 Mockito。这是最简单的解决方案(我在撰写本文时能想到)但它不能确保 @Autowired 注释不会在消费者中被遗忘(也许可以添加专门的测试?!):

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) 或者正如您所说,您可以 运行 它与 SpringJunitRunner 一起定义和实例化必要的 [= =45=] 上下文,同时还提供您自己的服务模拟。尽管人们可能会抱怨这个解决方案 不是那么干净 ,但我发现它足够优雅并且它还验证了 @Autowired 注释在消费者实现中没有被遗忘。

@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;
    }
}