英文:
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();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论