【问题标题】:Mockito mocked method returns nullMockito 模拟方法返回 null
【发布时间】:2020-01-21 20:25:43
【问题描述】:

我有一个我认为非常标准的 StorageService,它存在于用于 Web API 的 Spring Boot 应用程序中。

@Component
@Service
@Slf4j
public class StorageService {
    @Autowired
    private AmazonS3 s3Client;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${app.aws.s3.bucket}")
    private String bucket;

    @Async
    public boolean fetchAndUpload(List<URI> uris) {
        List<CompletableFuture<PutObjectResult>> futures = uris.stream().map(uri ->
                fetchAsync(uri).thenApplyAsync((asset) -> put(getName(uri.toString()), asset))
        ).collect(Collectors.toList());

        CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();

        return true;
    }

    @Async
    private CompletableFuture<InputStream> fetchAsync(URI uri) {
        return CompletableFuture.supplyAsync(() -> {
            InputStream resp;
            try {
                // asdf is null here when running my unit tests
                Resource asdf = restTemplate.getForObject(uri, Resource.class);
                resp = Objects.requireNonNull(asdf).getInputStream();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return resp;
        });
    }

    private PutObjectResult put(String name, InputStream data) {
        PutObjectRequest request = new PutObjectRequest(bucket, name, data, new ObjectMetadata());
        return s3Client.putObject(request);
    }
}

这是一个集成测试,它至少可以成功获取集成测试给出的图像:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWebClient
public class StorageServiceIT {
    @Value("${app.aws.s3.access.key}")
    private String accessKey;

    @Value("${app.aws.s3.secret.key")
    private String secretKey;

    @Spy
    private AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
            .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
            .withRegion(Regions.US_EAST_1)
            .build();

    @Spy
    private RestTemplate restTemplate = new RestTemplateBuilder().build();

    @MockBean
    private SignService signingService;

    @Autowired
    private StorageService storageService;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void fetchAsync() throws URISyntaxException {
        List<URI> uris = List.of(
                new URI("https://upload.wikimedia.org/wikipedia/commons/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg"),
                new URI("https://upload.wikimedia.org/wikipedia/commons/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg")
        );

        storageService.fetchAndUpload(uris);
    }
}

但是,以下单元测试没有成功模拟 restTemplate.getForObject 调用,它不断返回 null,即使将两个参数都设置为 any()

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWebClient
public class StorageServiceTest {
    @MockBean
    private AmazonS3 s3Client;

    @MockBean
    private RestTemplate restTemplate;

    @MockBean
    private SignService signingService;

    @Autowired
    // @InjectMocks ???
    private StorageService storageService;

    @Value("${app.aws.s3.bucket}")
    private String bucket;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    List<URI> randomUris(int num) {
        final String base = "https://example.com/%s";
        return Stream.iterate(0, i -> i)
                .limit(num)
                .map(o -> URI.create(String.format(base, UUID.randomUUID().toString())))
                .collect(Collectors.toList());
    }

    @Test
    public void fetchAsyncTest() {
        List<URI> uris = randomUris(2);

        uris.forEach(uri -> {
            ByteArrayInputStream data = new ByteArrayInputStream(
                    Integer.toString(uri.hashCode()).getBytes());
            PutObjectRequest request = new PutObjectRequest(
                    bucket, getName(uri.toString()), data, new ObjectMetadata());

            Resource getResult = mock(Resource.class);
            try {
                doReturn(data).when(getResult).getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }

            doReturn(data).when(restTemplate).getForObject(any(), any());

//          none are working
//          doReturn(data).when(restTemplate).getForObject(eq(uri), eq(Resource.class));
//          doReturn(data).when(restTemplate).getForObject(uri, Resource.class);
//          when(restTemplate.getForObject(uri, Resource.class)).thenReturn(data);
//          when(restTemplate.getForObject(eq(uri), eq(Resource.class))).thenReturn(data);
//          when(restTemplate.getForObject(any(), any())).thenReturn(data);

            PutObjectResult res = new PutObjectResult();
            doReturn(res).when(s3Client).putObject(eq(request));

//            not working here as well, i think
//            doReturn(res).when(s3Client).putObject(request);
//            doReturn(res).when(s3Client).putObject(any());
//            when(s3Client.putObject(eq(request))).thenReturn(res);
//            when(s3Client.putObject(request)).thenReturn(res);
//            when(s3Client.putObject(any())).thenReturn(res);

        });

        boolean res = storageService.fetchAndUpload(uris);
    }
}

为了以防万一,这就是我构建RestTemplate的方式:

  @Bean
  public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder.build();
  }

我很难过,感谢所有建议! :|

【问题讨论】:

  • 在使用@SpringBootTest 时,您不需要@InjectMocksMockitoAnnotations.initMocks(this);
  • 啊,谢谢。虽然仍然看到嘲笑问题
  • 而且您的代码在fetchAsyncTest 中确实很乱
  • 你在哪个班有问题?这是StorageServiceIT 还是StorageServiceTest
  • 问题出在StorageServiceTestStorageServiceIT 成功下载图片。混乱/注释代码只是为了表明所有这些版本也都失败了

标签: java spring spring-boot mocking mockito


【解决方案1】:

对此进行跟进 - 我的问题是因为我尝试测试的方法被标记为 Spring Boot 的 @Async 注释,导致竞态条件和一些模拟配置不正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    相关资源
    最近更新 更多