繁体   English   中英

如何在不调用外部 API 和使用依赖注入的情况下使用 RestTemplate 测试 Java/Spring 服务?

[英]How to test a Java/Spring service using RestTemplate without calling external API and by using Dependency Injection?

在我的 Spring 应用程序中,我有一个服务MyService MyService调用外部 API,计算那里的产品并返回结果。 要调用它 API 它使用 Spring 模块RestTemplate 要注入RestTemplate ,它在DependencyConfig中配置为@Bean

@Service
public class ServiceImpl implements MyService {

    private final RestTemplate restTemplate;

    public ServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public String serve() {
        ShopProductsResponse result = restTemplate.getForObject(
                "https://api.predic8.de/shop/products/",
                ShopProductsResponse.class
        );

        if (result == null) {
            return "Error occurred";
        }

        return String.format("Found %s products", result.getProducts().length);
    }
}

现在我想对其进行测试,无需调用外部 API。 所以我通过@Autowired注入MyService并通过RestTemplate注入@MockBean 要定义restTemplate.getForObject(...)的结果,我使用Mockito.when(...).thenReturn(...)方法来模拟结果:

@SpringBootTest
class ServiceTest {

    @MockBean
    private RestTemplate restTemplate;

    @Autowired
    private MyService service;

    @BeforeEach
    void init() {
        final ShopProductsResponse response = new ShopProductsResponse();
        response.setProducts(new Product[0]);

        Mockito.when(restTemplate.getForObject(Mockito.any(), Mockito.any())).thenReturn(response);
    }

    @Test
    void test() {
        final String expectation = "Found 0 products";

        String result = service.serve();

        Mockito.verify(restTemplate, Mockito.times(1)).getForObject(
                ArgumentMatchers.eq("https://api.predic8.de/shop/products/"),
                ArgumentMatchers.eq(ShopProductsResponse.class)
        );

        Assertions.assertEquals(expectation, result);
    }

}

问题是, restTemplate.getForObject(...)的结果是 null,因此测试失败并显示消息

org.opentest4j.AssertionFailedError: 
Expected :Found 0 products
Actual   :Error occurred

所以我的问题是,我做错了什么? 我以为我在告诉模拟要返回什么。 我该怎么做才能正确?

如果有人想尝试一下,我将示例项目推送到 Github: https://github.com/roman-wedemeier/spring_example

试图在网上找到答案让我很困惑,Junit(4/5) 有不同的版本。 另外,我在某个地方直接找到了有关 mocking 服务的教程,这不是我想做的。 另一方面,有人解释了如何模拟服务的依赖,但不使用 Spring 的依赖注入。

restTemplate.getForObject()方法有多个参数集,可以通过以下方式调用:

  • restTemplate.getForObject(String url, Class<T> responseType, Object... uriVariables)
  • restTemplate.getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
  • restTemplate.getForObject(URI url, Class<T> responseType)

因此,您可能通过提供最广泛的匹配器( Mockito.any() )来模拟另一个方法调用。 我建议您通过提供更具体的匹配器来尝试模拟restTemplate.getForObject()方法调用,例如:

Mockito.when(restTemplate.getForObject(Mockito.anyString(), Mockito.any())).thenReturn(response);

并且测试应该成功通过。

此外,您可以仅使用 Mockito 和 DI 对服务进行单元测试,而不是设置整个 Spring 应用程序上下文(由@SpringBootTest注释完成)。 这里没有必要,它只会使测试持续更长时间。 这是实现测试的另一种方法:

class ServiceTest {

    private RestTemplate restTemplate;
    private MyService service;

    @BeforeEach
    void init() {
        final ShopProductsResponse response = new ShopProductsResponse();
        response.setProducts(new Product[0]);

        this.restTemplate = Mockito.mock(RestTemplate.class);
        Mockito.when(this.restTemplate.getForObject(Mockito.anyString(), Mockito.any())).thenReturn(response);

        this.service = new ServiceImpl(restTemplate);
    }

    @Test
    void test() {
        final String expectation = "Found 0 products";

        String result = service.serve();

        Mockito.verify(restTemplate, Mockito.times(1)).getForObject(
                ArgumentMatchers.eq("https://api.predic8.de/shop/products/"),
                ArgumentMatchers.eq(ShopProductsResponse.class)
        );

        Assertions.assertEquals(expectation, result);
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM