使用junit5和Mockito模拟reactor.netty.http.client.HttpClient

问题描述 投票:0回答:1

我目前正在尝试使用 JUnit5 和 Mockito 测试我的 Java Spring Boot 应用程序中的一个类。问题在于其中一个类方法使用了reactor netty HttpClient。我在嘲笑 httpclient 时遇到一些问题。我正在使用 webclient 模拟中的一个方法,其中我模拟了 httpclient 上的每个 scall。现在我收到以下错误:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
   Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.

我的测试如下:

import io.netty.handler.codec.http.HttpMethod;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Spy;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufFlux;
import reactor.netty.ByteBufMono;
import reactor.netty.http.client.HttpClient;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

public class ServiceTest {

    @Spy
    private HttpClient httpClient;
    @Mock
    HttpClient.RequestSender requestSender;
    @Mock
    ByteBufFlux byteBufFlux;
    @Mock
    ByteBufMono byteBufMono;

    @BeforeEach
    void setup() {
        httpClient = HttpClient.create();
    }

    @Test
    void testFetchImageFromURLBase64Success() {
        mockStatic( HttpClient.class );
        mockHttpClient();


        var host = "example.com";
        var port = 80;
        var imageUrl = "http://example.com/image.jpg";

        when( httpClient.request( HttpMethod.GET ) )
                .thenReturn( requestSender );

        var res = Service.fetchImageFromURLBase64( host, port, imageUrl );

        assertNotNull( res );
    }

    private void mockHttpClient() {
        var mockImageBytes = new byte[]{ (byte)0x89, (byte)0x50 };


        when( httpClient.request( HttpMethod.GET ) )
                .thenReturn( requestSender );
        when( requestSender.uri( anyString() ) )
                .thenReturn( requestSender );
        when( requestSender.responseContent() )
                .thenReturn( byteBufFlux );
        when( byteBufFlux.aggregate() )
                .thenReturn( byteBufMono );
        when( byteBufMono.asByteArray() )
                .thenReturn( Mono.just( mockImageBytes ) );
    }
}

我想测试使用 httpclient 的方法如下所示:

public static String fetchImageFromURLBase64( String hostName, int port, String imageUrl ) {
        try {
            var httpClient = HttpClient.create()
                    .proxy( p -> p.type( ProxyProvider.Proxy.HTTP ).host( hostName ).port( port ) );
            var response = httpClient
                    .request( HttpMethod.GET )
                    .uri( imageUrl )
                    .responseContent()
                    .aggregate()
                    .asByteArray()
                    .block();

            if( response == null || response.length == 0 ) {
                log.error( "Could not retrieve image bytes from url: {}", imageUrl );
                return null;
            }

            return Base64.getEncoder().encodeToString( response );
        } catch( Exception e ) {
            // In case the service returns a 500 we don't want the application to crash
            log.error( "Error retrieving image from URL: {} with error message: ", imageUrl, e );
            return null;
        }
    }

有人知道为什么我会收到一般错误吗?或者是否有更好/更简单的方法来模拟 httpclient 调用?

java spring junit mockito httpclient
1个回答
0
投票

您的

@Spy
注释无用,原因有两个:

  1. 您的测试未使用 MockitoExtension 运行,因此注释完全被忽略
  2. 您的设置方法会重新分配该字段并用真实的、未监视的实例覆盖其值

假设您已加载 Mockito 扩展:

@ExtendWith(MockitoExtension.class)
public class ServiceTest {
    @Spy
    private HttpClient httpClient;

    @BeforeEach
    void setup() {
        httpClient = HttpClient.create();
    }

    @Test
    void test() {
        // …
    }
}

那么当你的测试执行时会发生什么?

  1. 创建了
    ServiceTest
    的新实例
  2. 扩展程序拾取所有 Mockito 注释,并创建 Mock 或 Spy 对象
  3. 实例的
    httpClient
    字段被赋值为创建的Spy对象
  4. 您的设置方法运行,用对使用
    HttpClient.create()
  5. 创建的新的真实实例的引用覆盖对 Spy 对象的引用
  6. 您的测试用例使用真正的http客户端,因为Spy对象不再可以从您的测试类访问(它的所有引用都消失了)

更详细的解释和问题的几种解决方案可以在为什么在执行单元测试时没有调用我的模拟方法?

© www.soinside.com 2019 - 2024. All rights reserved.