[英]Spring Webflux how to Mock response as Mono.error for WebClient Junit
[英]Spring Webflux Mockito - mock the response of a Webclient call
关于如何在单元测试阶段“强制”或“模拟”Webclient http 调用的响应的小问题。
我有一个非常简单的方法:
public String question() {
String result = getWebClient().mutate().baseUrl(someUrlVariable).build().post().uri("/test").retrieve().bodyToMono(String).block();
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
如您所见,此方法的复杂部分是 Webclient 调用。 它有(在这个例子中)7.method() like.mutate(), .post()。 ETC...
在我的用例中,我根本没有兴趣测试这个 Webclient。
我希望 Mockito 拥有的东西在某种程度上相当于:
public String question() {
// it is just unit test. Mockito, please just return me the string I tell you to return please. Don't even execute this next line if possible, just return me this dummy response
String result = the-thing-I-tell-mockito-to-return;
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
到目前为止,我尝试了整行 plus.thenReturn 的 Mockito doNothing() 或 Mockito.when(getWebclient()... ),但没有运气。
请问如何实现?
谢谢
您必须首先确保getWebclient()
返回一个模拟。 根据您现有的代码示例,我无法判断这是针对不同的 class 还是私有方法(通过构造函数注入WebClient
或WebClient.Builder
可能有意义)。
接下来,您必须使用 Mockito 模拟整个方法链。 这包括几乎复制/粘贴您的整个实现:
when(webClient.mutate()).thenReturn(webClient);
when(webClient.baseUrl(yourUrl)).thenReturn(...);
// etc.
Mockito 可以返回可以简化此存根设置的深度存根(检查文档并搜索RETURN_DEEP_STUBS
)。
但是,更好的解决方案是为您的WebClient
测试生成本地 HTTP 服务器并模拟 HTTP 响应。 这涉及较少的 Mockito 仪式并且还允许测试错误场景(不同的 HTTP 响应、慢响应等),
我想避免复制/粘贴 when()
好吧,您已经设计了代码,因此测试它的唯一方法是复制粘贴when
。
那么你是如何设计的呢? 好吧,您将 API 代码与逻辑混合在一起,这是您不应该做的事情。 编写测试时首先需要考虑的是“我要测试什么? ”而答案通常是业务逻辑。
如果我们查看您的代码:
public String question() {
// This is api code, we dont want to test this,
// spring has already tested this for us.
String result = getWebClient()
.mutate()
.baseUrl(someUrlVariable)
.build()
.post()
.uri("/test")
.retrieve()
.bodyToMono(String)
.block();
// This is logic, this is want we want to test
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
}
当我们设计一个应用程序时,我们将它分为几层,通常是前面的 api(RestController),然后是中间的业务逻辑(Controllers),最后是调用其他 API 的不同资源(存储库、资源等)。
因此,当涉及到您的应用程序时,我会重新设计它,将 api 和逻辑分开:
@Bean
@Qualifier("questionsClient")
public WebClient webclient(WebClient.Builder webClient) {
return webClient.baseUrl("https://foobar.com")
.build();
}
// This class responsibility is to fetch, and do basic validation. Ensure
// That whatever is returned from its functions is a concrete value.
// Here you should handle things like basic validation and null.
@Controller
public class QuestionResource {
private final WebClient webClient;
public QuestionResource(@Qualifier("questionsClient") WebClient webClient) {
this.webClient = webClient;
}
public String get(String path) {
return webClient.post()
.uri(path)
.retrieve()
.bodyToMono(String)
.block();
}
}
// In this class we make business decisions on the values we have.
// If we get a "Foo" we do this. If we get a "Bar" we do this.
@Controller
public class QuestionHandler {
private final QuestionResource questionResource;
public QuestionResource(QuestionResource questionResource) {
this.questionResource = questionResource;
}
public String get() {
final String result = questionResource.get("/test");
// also i dont see how the response can be null.
// Null should never be considered a value and should not be let into the logic.
// Because imho. its a bomb. Anything that touches null will explode (NullPointerException).
// Null should be handled in the layer before.
if (result == null) {
return doSomething1();
}
if (result.equals("")) {
return doSomething2();
}
if (result.equals("foo")) {
return doSomething3();
}
}
}
然后在你的测试中:
@Test
public void shouldDoSomething() {
final QuestionResource questionResourceMock = mock(QuestionResource.class);
when(questionResourceMock.get("/test")).thenReturn("");
final QuestionHandler questionHandler = new QuestionHandler(questionResourceMock);
final String something = questionHandler.get();
// ...
// assert etc. etc.
}
另外,我建议你不要改变 webclients,为每个 api 创建一个 webclient,因为它很快就会变得混乱。
这是在没有 IDE 的情况下编写的,因此可能存在编译错误等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.