junit 5不模拟java.net.http.HttpClient.send

huangapple go评论68阅读模式
英文:

junit 5 is not mocking java.net.http.HttpClient.send

问题

I can help with that. Here's the translated code:

我正在尝试在一个Java Maven项目而不是Spring Boot项目中编写JUnit 5代码该代码调用一个API并提取响应并创建一个文件但是当我尝试模拟API调用时JUnit仍然会调用实际的API这不是期望的行为有人能帮忙找出问题所在吗

我遵循了这个链接来编写下面的单元测试 https://stackoverflow.com/questions/69768931/how-to-mock-httpclients-send-method-in-junit-5

实际的服务代码 -

public class MyService {

    public void create(String apiHost, String firstPage) {
        HttpClient httpClient = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .connectTimeout(Duration.ofSeconds(30))
                .build();
        HttpRequest httpRequest = getHttpRequest(apiHost, firstPage);

        MyResponse myResponse = invokeApi(httpClient, httpRequest);
        List<ResponseData> responseDataList = new ArrayList<>(myResponse.getData());
        while (null != myResponse.getLinks().getNext()) {

            myResponse = invokeApi(httpClient, getHttpRequest(apiHost, invokeApi.getLinks().getNext()));
            locationDetailsList.addAll(invokeApi.getData());
        }

        createFile(fileName, responseDataList);
    }

    private HttpRequest getHttpRequest(String apiHost, String pageUri) {
        return HttpRequest.newBuilder()
                .header("Content-Type", "application/json")
                .GET()
                .uri(URI.create(apiHost + pageUri))
                .build();
    }

    @SneakyThrows
    MyResponse invokeApi(HttpClient httpClient, HttpRequest httpRequest) {
        HttpResponse<Supplier<MyResponse>> result = httpClient.send(httpRequest, new JsonBodyHandler<>(MyResponse.class));
        return result.body().get();
    }

    @SneakyThrows
    void createFile(String fileName, List<ResponseData> responseDataList) {
        Path filePath = Paths.get("test/path" + fileName);
        Files.createFile(filePath);
        Files.writeString(filePath, header + System.lineSeparator(), StandardOpenOption.APPEND);
        for (ResponseData responseData : responseDataList) {
            Files.writeString(filePath, responseData + System.lineSeparator(), StandardOpenOption.APPEND);
        }
    }
}

JUnit 5 代码 -

@ExtendWith(MockitoExtension.class)
class MyServiceTests {

    @InjectMocks
    private MyService myService;

    @Mock
    private HttpClient httpClient;
    @Mock
    HttpResponse<Object> httpResponse;

    @Mock
    private HttpRequest request;

    @Mock
    Supplier<MyResponse> myResponseSupplier;


    @Test
    void testSendMessage() throws Exception {

        when(httpClient.send(any(),any())).thenReturn(httpResponse);
        when(httpResponse.body()).thenReturn(myResponseSupplier);
        when(myResponseSupplier.get()).thenReturn(getMyResponse());
        myService.create("https://www.google.com", "/v1/test?page_size=100");
        verify(httpClient).send(request, HttpResponse.BodyHandlers.ofString());
    }
}

Remember, it's crucial to understand that merely translating code doesn't solve the issue you described. You should continue debugging the issue related to the actual API call.

英文:

I am trying to write junit 5 code in a java maven project(not a springboot project) , which is calling an API and extracting response and creating a file. But when I tried to mock the API calling, junit still calls actual API, which is not the desired behaviour. Can someone help to identify issue here?

I followed this link to write below unit tests https://stackoverflow.com/questions/69768931/how-to-mock-httpclients-send-method-in-junit-5

Actual service code -

public class MyService {
public void create(String apiHost, String firstPage) {
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(30))
.build();
HttpRequest httpRequest = getHttpRequest(apiHost, firstPage);
MyResponse myResponse = invokeApi(httpClient, httpRequest);
List<ResponseData> responseDataList = new ArrayList<>(myResponse.getData());
while (null != myResponse.getLinks().getNext()) {
myResponse = invokeApi(httpClient, getHttpRequest(apiHost, invokeApi.getLinks().getNext()));
locationDetailsList.addAll(invokeApi.getData());
}
createFile(fileName, responseDataList);
}
private HttpRequest getHttpRequest(String apiHost, String pageUri) {
return HttpRequest.newBuilder()
.header("Content-Type", "application/json")
.GET()
.uri(URI.create(apiHost + pageUri))
.build();
}
@SneakyThrows
MyResponse invokeApi(HttpClient httpClient, HttpRequest httpRequest) {
HttpResponse<Supplier<MyResponse>> result = httpClient.send(httpRequest, new JsonBodyHandler<>(MyResponse.class));
return result.body().get();
}
@SneakyThrows
void createFile(String fileName, List<ResponseData> responseDataList) {
Path filePath = Paths.get("test/path" + fileName);
Files.createFile(filePath);
Files.writeString(filePath, header + System.lineSeparator(), StandardOpenOption.APPEND);
for (ResponseData responseData : responseDataList) {
Files.writeString(filePath, responseData + System.lineSeparator(), StandardOpenOption.APPEND);
}
}

Junit 5 code -

@ExtendWith(MockitoExtension.class)
class MyServiceTests {
@InjectMocks
private MyService myService;
@Mock
private HttpClient httpClient;
@Mock
HttpResponse<Object> httpResponse;
@Mock
private HttpRequest request;
@Mock
Supplier<MyResponse> myResponseSupplier;
@Test
void testSendMessage() throws Exception {
when(httpClient.send(any(),any())).thenReturn(httpResponse);
when(httpResponse.body()).thenReturn(myResponseSupplier);
when(myResponseSupplier.get()).thenReturn(getMyResponse());
myService.create("https://www.google.com", "/v1/test?page_size=100");
verify(httpClient).send(request, HttpResponse.BodyHandlers.ofString());
}
}

In the above junit code when "myService.createmyMnt" is executed it actually tried to invoke https://test.com instead of mocking the call.

答案1

得分: 2

你面对这个问题是因为你两次模拟了相同的依赖项。
这是我的意思:

 @Mock
private HttpClient httpClient;

然后在 beforeEach 中,你再次模拟了你的依赖项

httpClient = mock(HttpClient.class);

我建议选择你需要的方法,不要模拟两次。
这里你在模拟和使用 @InjectMocks。另外我们需要添加 @ExtendWith(MockitoExtension.class) 注解,这将处理 initMocks(this) 并且我们不需要添加它(这个注解帮助模拟正确注入)。

@ExtendWith(MockitoExtension.class)
class MyServiceTests {
@InjectMocks
private MyService myService;
@Mock
private HttpClient httpClient;
@Mock
HttpResponse<Object> httpResponse;
@Mock
private HttpRequest request;
@Test
void testSendMessage() throws Exception {
MyResponse myResponse=new MyResponse();
when(httpClient.send(any(),any())).thenReturn(httpResponse);
when(myService.invokeApi(httpClient,request)).thenReturn(myResponse);
myService.createmyMnt("https://test.com", "/v1/test?page_size=100");
verify(httpClient).send(request, HttpResponse.BodyHandlers.ofString());
}
}

或者像这样使用构造函数注入:

class MyServiceTests {
private MyService myService;
private HttpClient httpClient;
private HttpResponse<Supplier<MyResponse>> httpResponse;
private HttpRequest request;
@BeforeEach
void setUp() {
httpClient = mock(HttpClient.class);
httpResponse = mock(HttpResponse.class);
request = mock(HttpRequest.class);
myService = new MyService();
}
@Test
void testSendMessage() throws Exception {
MyResponse myResponse = new MyResponse();
when(httpClient.send(any(), any())).thenReturn(httpResponse);
when(httpResponse.body()).thenReturn(() -> myResponse);
myService.create("https://test.com", "/v1/test?page_size=100");
verify(httpClient).send(request, new JsonBodyHandler<>(MyResponse.class));
}
}

根据评论,请尝试以下操作:

    @ExtendWith(MockitoExtension.class)
class MyServiceTests {
@InjectMocks
private MyService myService;
@Mock
private HttpClient httpClient;
@Mock
private HttpResponse<Supplier<MyResponse>> httpResponse;
@Mock
private HttpRequest request;
@Mock
private Supplier<MyResponse> myResponseSupplier;
@Test
void testSendMessage() throws Exception {
MyResponse myResponse = getMyResponse();
when(httpClient.send(any(), any())).thenReturn(httpResponse);
when(httpResponse.body()).thenReturn(() -> myResponseSupplier);
when(myResponseSupplier.get()).thenReturn(myResponse);
myService.create("https://www.google.com", "/v1/test?page_size=100");
verify(httpClient).send(request, HttpResponse.BodyHandlers.ofString());
}
private MyResponse getMyResponse() {
return new MyResponse();
}
}
英文:

You are facing this because you are mocking the same dependencies 2 times.
Here what i mean:

 @Mock
private HttpClient httpClient;

And then in beforeEach you again are mocking your dependency

httpClient = mock(HttpClient.class);

I recommend choosing the approach that you need and don't mock 2 times.
Here where you are mocking and use @InjectMocks. Also we need to add annotation @ExtendWith(MockitoExtension.class) this will handle initMocks(this) and we won't need to add it(this annotation helps to mocks be properly injected).

@ExtendWith(MockitoExtension.class)
class MyServiceTests {
@InjectMocks
private MyService myService;
@Mock
private HttpClient httpClient;
@Mock
HttpResponse<Object> httpResponse;
@Mock
private HttpRequest request;
@Test
void testSendMessage() throws Exception {
MyResponse myResponse=new MyResponse();
when(httpClient.send(any(),any())).thenReturn(httpResponse);
when(myService.invokeApi(httpClient,request)).thenReturn(myResponse);
myService.createmyMnt("https://test.com", "/v1/test?page_size=100");
verify(httpClient).send(request, HttpResponse.BodyHandlers.ofString());
}
}

or use constructor injection like:

class MyServiceTests {
private MyService myService;
private HttpClient httpClient;
private HttpResponse<Supplier<MyResponse>> httpResponse;
private HttpRequest request;
@BeforeEach
void setUp() {
httpClient = mock(HttpClient.class);
httpResponse = mock(HttpResponse.class);
request = mock(HttpRequest.class);
myService = new MyService();
}
@Test
void testSendMessage() throws Exception {
MyResponse myResponse = new MyResponse();
when(httpClient.send(any(), any())).thenReturn(httpResponse);
when(httpResponse.body()).thenReturn(() -> myResponse);
myService.create("https://test.com", "/v1/test?page_size=100");
verify(httpClient).send(request, new JsonBodyHandler<>(MyResponse.class));
}
}

According to the comments try this please:

    @ExtendWith(MockitoExtension.class)
class MyServiceTests {
@InjectMocks
private MyService myService;
@Mock
private HttpClient httpClient;
@Mock
private HttpResponse<Supplier<MyResponse>> httpResponse;
@Mock
private HttpRequest request;
@Mock
private Supplier<MyResponse> myResponseSupplier;
@Test
void testSendMessage() throws Exception {
MyResponse myResponse = getMyResponse();
when(httpClient.send(any(), any())).thenReturn(httpResponse);
when(httpResponse.body()).thenReturn(() -> myResponseSupplier);
when(myResponseSupplier.get()).thenReturn(myResponse);
myService.create("https://www.google.com", "/v1/test?page_size=100");
verify(httpClient).send(request, HttpResponse.BodyHandlers.ofString());
}
private MyResponse getMyResponse() {
return new MyResponse();
}
}

huangapple
  • 本文由 发表于 2023年5月22日 13:56:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76303358.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定