[英]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.