使用Mockito在另一个Spring服务中模拟服务

12

我遇到了在Spring框架中注入其他服务的服务进行模拟测试的问题。以下是我的代码:

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

    @Autowired
    private ClientService clientService;

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

我想在我的测试中嘲笑ClientService,所以我尝试了以下操作:

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

productService内的clientService是Spring代理版本,而不是我想要的模拟版本。是否有可能使用Mockito实现我想要的内容?

4个回答

6

您需要在ProductService上注解@InjectMocks

@Autowired
@InjectMocks
private ProductService productService;

这将会把ClientService的模拟对象注入到你的ProductService中。

谢谢Debojit,但我意识到无法将Spring的注入与Mockito的模拟注入结合使用...所以我的解决方案是手动创建ProductService,并像你说的那样使用@InjectMocks注入模拟对象,但不使用@Autowired@InjectMocks private ProductService productService = new ProductServiceImpl(); - br4zuca
很奇怪,我一直在同时使用这两个注释。你测试过吗?在同时使用这两个注释时有任何异常吗? - Debojit Saikia
是的,我进行了很多测试,即使在ProductService上使用了@InjectMocks,其中的clientService也是由Spring代理加载而不是模拟对象。也许你使用的spring-test版本与我使用的不同,我的版本是3.0.1.RELEASE,你的版本是什么? - br4zuca
我正在使用3.1.2.RELEASE和mockito 1.9.5。 - Debojit Saikia
我已经升级到3.1.2-RELEASE版本,但结果仍然相同...也许你的配置在某些方面与我的不同,无论如何,感谢你的帮助,现在我将使用手动实例化方法。 - br4zuca
如果clientService再次使用repository自动装配(productService> clientService> xyzRepository),并且我们需要模拟repo(xyzRepository),那么需要做什么来进行模拟? - Satish Patro

1
除此之外,

@Autowired
@InjectMocks
private ProductService productService;

添加以下方法。
@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

如果clientService再次使用了repository自动装配(productService > clientService > xyzRepository),并且我们需要Mock这个repo(xyzRepository),需要做什么来Mock它? - Satish Patro

1
我建议您在Test target上注释@InjectMock 目前
    @Autowired
    private ProductService productService;

    @Mock
    private ClientService clientService;

改为

    @InjectMock
    private ProductService productService;

    @Mock
    private ClientService clientService;

如果您仍然在MockingService中遇到NullPointerException => 您可以使用Mockito.any()作为参数。希望这能帮助您。

如果clientService再次使用了自动装配的repository(productService > clientService > xyzRepository),并且我们需要模拟该repo(xyzRepository),那么需要做什么来进行模拟? - Satish Patro
@SatishPatro,我需要实现类似的东西。你找到解决方法了吗? - tez
@tez 我们需要逐步进行。如果控制器调用服务,那么服务将调用存储库。在控制器测试类中,您可以模拟服务。在服务测试类中,您可以模拟存储库。就像每个类都有自己的测试类一样。 - Satish Patro

1

有更多的方法可以实现这个功能,最简单的方法是使用setter注入而不是字段注入,这意味着您应该有:

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

如果在您的服务类中,您可以将模拟对象注入到测试类中的服务中:

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

重要提示:如果这只是一个单元测试,请考虑不使用SpringJUnit4ClassRunner.class,而是使用MockitoJunitRunner.class,这样您也可以为您的字段使用字段注入。


1
谢谢,我尝试了那个解决方案并且运行得很好,但是我不能在那些服务中改成setter注入,所以我的解决方案是在我的测试中删除@Autowired注释并手动创建服务,就像下面这样:@InjectMocks private ProductService productService = new ProductServiceImpl(); - br4zuca
@br4zuca,你可以使用@InjectMocks private ProductService productService来完成它,不要手动初始化,让Mockito来完成。 - Jaiwo99
1
我尝试使用@InjectMocks来初始化productService,但是由于ProductService是一个接口,它抛出了org.mockito.exceptions.base.MockitoException: the type 'ProductService' is an interface的异常,因此无法实例化。 - br4zuca
2
我找到了一个解决方案,不是声明 @InjectMock ProductService productService,而是将 ProductService 接口改为实现类 ProductServiceImpl,这样 Mockito 就可以自动初始化我的服务,像这样:@InjectMock ProductServiceImpl productService - br4zuca
如果clientService再次使用repository自动装配(productService> clientService> xyzRepository),并且我们需要模拟repo(xyzRepository),那么需要做什么来进行模拟? - Satish Patro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接