英文:
In unit tests, why does mocked s3Object.getObjectContent() return empty after first call?
问题
以下是您提供的代码的翻译部分:
我已经制作了以下JUnit5/Mockito测试代码,可以轻松自包含地复制并粘贴;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Builder;
import lombok.Data;
import org.apache.http.client.methods.HttpRequestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.WARN)
public class S3GetObjectContentTest {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Mock private HttpRequestBase httpRequestBase;
@Mock private S3Object s3Object;
private final LinkedHashSet<Pet> s3FileContents = new LinkedHashSet<>();
@BeforeEach
void setup() throws IOException {
s3FileContents.clear();
s3FileContents.addAll(
List.of(
Pet.builder().name("Jerry").type("mouse").build(),
Pet.builder().name("Tom").type("cat").build(),
Pet.builder().name("Roger").type("bunny").build()));
Mockito.when(s3Object.getObjectContent())
.thenReturn(
new S3ObjectInputStream(
new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
httpRequestBase));
}
@Test
public void doTest() {
for(int n=0; n < 3; n++){
try (final InputStreamReader streamReader =
new InputStreamReader(s3Object.getObjectContent(), StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(streamReader)) {
String text = reader.lines().collect(Collectors.joining());
System.out.println("reader-content: "+ (text.isEmpty() ? "\"\"" : text));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Data
@Builder
public static class Pet {
public final String name;
public final String type;
}
}
当运行上述测试代码时,预期输出如下:
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
然而,实际输出如下:
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
reader-content: ""
reader-content: ""
问题:
为什么s3Object.getObjectContent()
的模拟仅在第一次调用时生效,之后为空?
注意:
实验性地(从不理想的角度),我观察到只有将模拟放在for...loop
内时才能获得预期结果,这可能会提供一些额外的背景信息。
@Test
public void doTest() throws JsonProcessingException {
for(int n=0; n < 3; n++){
// ========================== 实验性(不实用) ==========================
Mockito.when(s3Object.getObjectContent())
.thenReturn(
new S3ObjectInputStream(
new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
httpRequestBase));
// =====================================================================
try (final InputStreamReader streamReader =
new InputStreamReader(s3Object.getObjectContent(), StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(streamReader)) {
String text = reader.lines().collect(Collectors.joining());
System.out.println("reader-content: "+ (text.isEmpty() ? "\"\"" : text));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
英文:
I have made the following JUnit5/Mockito Test code an easy self-contained copy & paste;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Builder;
import lombok.Data;
import org.apache.http.client.methods.HttpRequestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.WARN)
public class S3GetObjectContentTest {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Mock private HttpRequestBase httpRequestBase;
@Mock private S3Object s3Object;
private final LinkedHashSet<Pet> s3FileContents = new LinkedHashSet<>();
@BeforeEach
void setup() throws IOException {
s3FileContents.clear();
s3FileContents.addAll(
List.of(
Pet.builder().name("Jerry").type("mouse").build(),
Pet.builder().name("Tom").type("cat").build(),
Pet.builder().name("Roger").type("bunny").build()));
Mockito.when(s3Object.getObjectContent())
.thenReturn(
new S3ObjectInputStream(
new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
httpRequestBase));
}
@Test
public void doTest() {
for(int n=0; n < 3; n++){
try (final InputStreamReader streamReader =
new InputStreamReader(s3Object.getObjectContent(), StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(streamReader)) {
String text = reader.lines().collect(Collectors.joining());
System.out.println("reader-content: "+ (text.isEmpty() ? "\"\"" : text));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Data
@Builder
public static class Pet {
public final String name;
public final String type;
}
}
When the above Test code is run, the following output is expected;
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
However the actual output seen is;
reader-content: [{"name":"Jerry","type":"mouse"},{"name":"Tom","type":"cat"},{"name":"Roger","type":"bunny"}]
reader-content: ""
reader-content: ""
<h1>Question:</h1>
Why is the Mock for s3Object.getObjectContent()
kicking-in only on the first call and barfing with empty afterwards?
<h3>NOTE:</h3>
Experimentally (never ideal), I observed that I only get the expected result when I put the mock inside the for...loop
... Suppose this might just give some extra context
@Test
public void doTest() throws JsonProcessingException {
for(int n=0; n < 3; n++){
// ========================== Experimental (Not practical) ==========================
Mockito.when(s3Object.getObjectContent())
.thenReturn(
new S3ObjectInputStream(
new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
httpRequestBase));
// ==================================================================================
try (final InputStreamReader streamReader =
new InputStreamReader(s3Object.getObjectContent(), StandardCharsets.UTF_8);
final BufferedReader reader = new BufferedReader(streamReader)) {
String text = reader.lines().collect(Collectors.joining());
System.out.println("reader-content: "+ (text.isEmpty() ? "\"\"" : text));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
答案1
得分: 1
我怀疑在第一次运行后,流已关闭,因此您无法再获取更多数据。您的beforeEach方法只运行一次,而不是三次。您可以通过将“when”语句从设置方法移动到for循环中来解决此问题。
英文:
I suspect that after first run, the stream is closed so you don’t get any more data out of it. Your beforeEach method only runs once, not three times. You could solve this by moving the “when” statement from setup method into the for loop.
答案2
得分: 1
原来解决方案并不是真的“远离家园” 😊。
只需基于以下调用来模拟s3Object.getObjectContent()
:
Mockito.when(s3Object.getObjectContent())
.thenAnswer(invocation -> {
return new S3ObjectInputStream(
new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
httpRequestBase);
});
特别感谢Tofig Hasanov的快速见解,帮助我修复了这个问题。
英文:
Well, turns out the solution wasn't really far-from-home 😊.
Simply had to mock s3Object.getObjectContent()
based on invocation as follows;
Mockito.when(s3Object.getObjectContent())
.thenAnswer(invocation -> {
return new S3ObjectInputStream(
new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
httpRequestBase);
});
Shout out to Tofig Hasanov for the quick insight that helped me fix this.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论