mockito 在单元测试中,为什么模拟的s3Object.getObjectContent()在第一次调用后返回空?

7bsow1i6  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(71)

我已经使下面的JUnit 5/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()的Mock只在第一次调用时启动,然后吐空?

注:

在实验上 (从来都不理想),我观察到,只有当我把mock放在for...loop里面时,我才能得到预期的结果...假设这可能只是给予一些额外的上下文

@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);
      }
    }
  }

a64a0gku

a64a0gku1#

我怀疑在第一次运行后,流被关闭,所以你不能从中获得更多的数据。beforeEach方法只运行一次,而不是三次。您可以通过将“when”语句从setup方法移到for循环中来解决这个问题。

sqyvllje

sqyvllje2#

好吧,事实证明解决方案并不是离家很远。😊
只需根据调用模拟s3Object.getObjectContent(),如下所示;

Mockito.when(s3Object.getObjectContent())
        .thenAnswer(invocation -> {
          return new S3ObjectInputStream(
                  new ByteArrayInputStream(OBJECT_MAPPER.writeValueAsBytes(s3FileContents)),
                  httpRequestBase);
        });

字符串
Tofig Hasanov 大喊,因为他的快速洞察力帮助我解决了这个问题。

相关问题