Mockito mocked метод возвращает ноль - PullRequest
0 голосов
/ 20 сентября 2019

У меня есть, как мне кажется, довольно стандартный StorageService, который существует в приложении Spring Boot для веб-API.

@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, он постоянно возвращает ноль, даже если для обоих аргументов установлено значение 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();
  }

Я в тупике, все советы приветствуются!: |

1 Ответ

0 голосов
/ 30 сентября 2019

Чтобы решить эту проблему - моя проблема заключалась в том, что метод, который я пытался протестировать, был помечен аннотацией @Async Spring Boot, что приводило к тому, что условия гонки и некоторые макеты не были настроены должным образом.

...