模拟 org.springframework.web.reactive.function.client.WebClient.ResponseSpec#onStatus 输入参数

Gre*_*000 2 java webclient mockito

考虑以下代码:

    public Mono<Void> doStuff() {

        return this.requestStuff()
            .onStatus(HttpStatus::is5xxServerError,
                    clientResponse -> {
                    aMethodIWouldLikeToTest(clientResponse);
                    return Mono.error(new MyCustomException("First error I would like to test"));
                    })
            .onStatus(HttpStatus::is4xxClientError,
                    clientResponse -> {
                    aMethodIWouldLikeToTest(clientResponse);
                    return Mono.error(new MyCustomException("Second error I would like to test"));
                    })
            .bodyToMono(String.class)
            .flatMap(x -> anotherMethodIManagedToTest(x)))

    }
Run Code Online (Sandbox Code Playgroud)

我的第一个目标是测试anotherMethodIManagedToTest(x),这是使用以下方法实现的:

    import org.springframework.web.reactive.function.client.WebClient;

    ...

    @Mock
    private WebClient.ResponseSpec responseSpec;

    private String desiredInputParam = "There is another black spot on the sun today!";

    ...

    @Test
    public void allGood_anotherMethodIManagedToTest_success {

        ...

        ClassUnderTest classUnderTest = new classUnderTest()
        ClassUnderTest classUnderTestSpy = spy(classUnderTestSpy);
        doReturn(responseSpec).when(classUnderTestSpy).requestStuff();

        when(responseSpec.onStatus(any(), any())).thenReturn(responseSpec);
        when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(desiredInputParam));

        Mono<Void> result = classUnderTestSpy.doStuff();

        // Bunch of assertions for anotherMethodIManagedToTest(String desiredInputParam) performed with success ...

    }
Run Code Online (Sandbox Code Playgroud)

现在我想创建额外的测试来测试 5xxServerError 事件和 4xxClientError 事件,但我很难弄清楚如何:

  • 模拟HttpStatus::is5xxServerError的响应
  • 模拟HttpStatus::is4xxServerError的响应
  • 模拟 clientResponse 以测试aMethodIWouldLikeToTest(org.springframework.web.reactive.function.client.ClientResponse clientResponse)

关于如何执行这些操作有什么建议吗?

请注意,我不能真正使用任何 PowerMock 替代品(如果这是实现我的目标的唯一方法,我仍然感兴趣)所有标准 Mockito 的答案都是首选。

小智 7

对于我迟到的回复,我深表歉意。可以将mockito用于您的测试用例:

  • 您需要获取 HttpStatus 才能在 onStatus 方法中检查它
  • 所以首先创建一个实现 WebClient.ResponseSpec 的抽象类(以避免实现所有方法)
  • 您可以从此类创建模拟而不是 ResponseSpec
  • 我在这个类中添加了2个方法:
abstract class CustomMinimalForTestResponseSpec implements WebClient.ResponseSpec {

        public abstract HttpStatus getStatus();

        public WebClient.ResponseSpec onStatus(Predicate<HttpStatus> statusPredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
            if (statusPredicate.test(this.getStatus())) exceptionFunction.apply(ClientResponse.create(HttpStatus.OK).build()).block();
            return this;
        }
      }
Run Code Online (Sandbox Code Playgroud)
  • getStatus 将用于设置 HttpStatus:
when(responseSpecMock.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
Run Code Online (Sandbox Code Playgroud)
  • 对于onStatus,您可以调用真正的方法:
when(responseSpecMock.onStatus(any(Predicate.class),any(Function.class)))
   .thenCallRealMethod();
Run Code Online (Sandbox Code Playgroud)
  • 这是测试类:
package com.example.test;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.function.Function;
import java.util.function.Predicate;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;


@ExtendWith(MockitoExtension.class)
public class ExperimentalWebClientTest {

    @Mock
    private WebClient webClientMock;

    @InjectMocks
    private ExperimentalWebClient experimentalWebClient;

    @Mock
    private WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock;

    @Mock
    private WebClient.RequestHeadersSpec requestHeadersSpecMock;

    @Mock
    private CustomMinimalForTestResponseSpec responseSpecMock;

    @Test
    void shouldFailsWhenHttpStatus5xx() {
        //given
        when(webClientMock.get()).thenReturn(requestHeadersUriSpecMock);
        when(requestHeadersUriSpecMock.uri(any(Function.class)))
            .thenReturn(requestHeadersSpecMock);
        when(requestHeadersSpecMock.retrieve()).thenReturn(responseSpecMock);

        when(responseSpecMock.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);

        when(responseSpecMock.onStatus(any(Predicate.class), any(Function.class))).thenCallRealMethod();

        //when + Then
        assertThrows(MyCustomException.class,
            () -> experimentalWebClient.doStuff(),
            "call fails with Internal Server Error") ;
    }


    abstract class CustomMinimalForTestResponseSpec implements WebClient.ResponseSpec {

        public abstract HttpStatus getStatus();

        public WebClient.ResponseSpec onStatus(Predicate<HttpStatus> statusPredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) {
            if (statusPredicate.test(this.getStatus())) exceptionFunction.apply(ClientResponse.create(HttpStatus.OK).build()).block();
            return this;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)