Mockito mocked method returns null - java

I have what I think is a pretty standard StorageService that exists in a Spring Boot app for a web 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);
}
}
Here is an integration test, which at minimum successfully fetches the images given by the integration test:
#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);
}
}
However, the following unit test does not successfully mock the restTemplate.getForObject call, it constantly returns null, even when setting both arguments to 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);
}
}
And just in case it's relevant, this is how I'm building the RestTemplate:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
I'm stumped, all advice is appreciated! :|

To follow up on this - my problem was because the method I was attempting to test was marked with Spring Boot's #Async annotation, causing race conditions and some mocks to not be configured properly.

Related

Getting null response of resttemplate exchange in a mockito test

i am trying to write the mock test case for RestServiceImpl class. Below is the code. And i have a test class shown below RestServiceImplTest. When i run the test class it returning null from line restTemplate.exchange(UrlString, HttpMethod.POST, request, Object.class)
public class RestServiceImpl
private RestTemplate restTemplate;
#Autowired
public RestServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public ResponseEntity<Object> restService(DataRequest dataRequest) throws Exception {
ResponseEntity<Object> response = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<DataRequest> request = new HttpEntity<>(dataRequest, headers);
try {
response = restTemplate.exchange(UrlString, HttpMethod.POST, request, Object.class);
} catch (Exception ex) {
throw ex;
}
return response;
}
}
Test class
#RunWith(SpringRunner.class)
public class RestServiceImplTest {
private RestServiceImpl restServiceImpl;
#Mock
private RestTemplate restTemplate;
#Mock
private DataRequest dataRequest;
#Before
public void setUp() {
restServiceImpl = new RestServiceImpl(restTemplate);
dataRequest = new DataRequest();
}
#Test
public void testRestServiceImplwithSuccess() throws Exception {
ResponseEntity<Object> resp = new ResponseEntity<Object>(HttpStatus.OK);
doReturn(resp).when(restTemplate).exchange(any(URI.class), any(HttpMethod.class), any(HttpEntity.class),
any(Class.class));
ResponseEntity<Object> mockResp = restServiceImpl.restService(DataRequest);
}
Can anybody tell me where is it going wrong.
Almost certainly this is happening because your doReturn is not being used due to the parameters not being met (your any() s).

Spring Boot Rest template Mocking : ResponseEntity.getBody() throws a Null pointer exception

I am new to JUnit and Mockito. Here I am trying to mock the rest template call and return a response entity. But it throws me a Null pointer Exception. I am not able to figure out what's wrong with the mocking. Can anyone guide me where I am doing wrong?
JUnit5 Unit test cases
class MyTest {
#InjectMocks
private MyService service;
#Mock
private RestTemplate template;
#BeforeEach
void setUp(){
MockitoAnnotations.initMocks(this);
}
private ResponseEntity<String> generateResponse(String body, HttpStatus http) {
return new ResponseEntity<>(body, http);
}
#Test
public void publishEventsuccessResponseTest() {
when(template.postForEntity(ArgumentMatchers.anyString(), ArgumentMatchers.any(),
ArgumentMatchers.<Class<String>>any())).thenReturn(generateResponse("uuid",
HttpStatus.OK));
String result = service.sentData("data");
Assertions.assertTrue("uuid".equals(result));
}
}
Service
class MyService {
public String sentData(String data) {
String jsonResp = "";
ResponseEntity<String> response;
try {
HttpEntity<String> requestBody = new HttpEntity<String>(data, headers);
response = restTemplate.postForEntity("url", requestBody, String.class);
} catch (Exception ex) {}
System.out.println(response); //value : Null
if (response.getStatusCodeValue() == 200) { // Null pointer exception
jsonResp = response.getBody();
}
return jsonResp;
}
}
Change the #BeforeEach to #Before of jUnit4 and add public void setUp()
Your test should be green!

Mock Test for AWS Secret Manager - Java

I am trying to test the AWS SecretManager call using Mockito but when I run the program, I am getting the Null Pointer Exception.
#ExtendWith(MockitoExtension.class)
class XXXX{
String secret = "{ \"client_id\": \"XXXXXX\",\"client_secret\": \"XXXXXX\"} ";
#Mock
AWSSecretsManager secretsClient;
#Mock
GetSecretValueRequest secretValueRequest;
#Mock
GetSecretValueResult secretValueResult;
#BeforeEach
public void setUp(){
lenient().when(secretsClient.getSecretValue(secretValueRequest)).thenReturn(secretValueResult);
lenient().when(secretValueResult.getSecretString()).thenReturn(secret);
}
}
Here, when I am running, I am getting the NullPointerException at when(secretsClient.getSecretValue(secretValueRequest)). It says as secretsClient.getSecretValue(secretValueRequest) is null which is passed as parameter to when(). Any suggestion or advice what I am doing wrong here, please.
You need to install the mockito extension via: #ExtendWith(MockitoExtension.class)
(I think you'll need to make the member variables non-private too).
More clues here: https://www.baeldung.com/mockito-junit-5-extension
Have you tried setting the value on the GetSecretVaueResult first then returning it; something like this?
#Mock
AWSSecretsManager secretsClient;
GetSecretValueResult secretValueResult = new GetSecretValueResult();
secretValueResult.setSecretString("{\"client_id\": \"XXXXXX\",\"client_secret\": \"XXXXXX\"}");
when(secretsClient.getSecretValue(any(GetSecretValueRequest.class))).thenReturn(secretValueResult);
I would determine how you are building your AWSSecretsManager instance within your getSecret() method.
Consider if you are using a getSecret() method similar to the one AWS provides like the following:
public static void getSecret() {
String secretName = "arn:aws:secretsmanager:us-east-1:xxxxxxx";
String region = "us-east-1";
// Create a Secrets Manager client
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
logger.error("Error retrieving secret: {0}", e);
throw e;
}
...
}
In this case, mocking AWSSecretsManager within your JUnit test will not have the desired outcome because the getSecret() method is instantiating AWSSecretsManagerClientBuilder and assigning it to client each time getSecret() is called. Instead, you can add a configuration class with an AWSSecretsManager bean as and then autowire it in the constructor of the class that contains the getSecret() method.
Add Configuration
#Configuration
public class Config {
#Value("${cloud.aws.region.static}")
private String region;
#Bean
public AWSSecretsManager awsSecretsManager(String region) {
return AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
}
}
Update getSecret()
After doing so, your method should look more like this
private String getSecret() {
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
logger.error("Error retrieving secret: {0}", e);
throw e;
}
...
}
Test
Now, you will be able to mock the AWSSecretsManager as intended:
#Mock
AWSSecretsManager client;
private final YourClass undertest;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
undertest = new YourClass(...)
}
#Test
void testYourClass() {
GetSecretValueResult expected = new GetSecretValueResult();
expected.setSecretString("{\"client_id\": \"XXXXXX\",\"client_secret\": \"XXXXXX\"}");
when(client.getSecretValue(any(GetSecretValueRequest.class)))
.thenReturn(expected);
...
}
solution is create real GetSecretValueResponse:
GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(secretValue).build();
so my test is:
public class AWSSecretsManagerTest {
#InjectMock SecretsManagerClient client;
#Inject AWSSecretsManager secretsManager;
#Test
void getSecret_GetSecretStringByName() {
// FIXTURE
var secretValue = "some-value";
GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(secretValue).build();
when(client.getSecretValue((GetSecretValueRequest) any())).thenReturn(response);
// exercise
var result = secretsManager.getSecret("some-secret");
//verify
Assertions.assertEquals(secretValue, result);
}
}
my manager:
#ApplicationScoped
public class AWSSecretsManager implements SecretsManager {
public static final String VERSION_STAGE = "AWSCURRENT";
#Inject
SecretsManagerClient secretsManagerClient;
private GetSecretValueRequest generateGetSecretValueRequest(String secretName) {
return GetSecretValueRequest.builder()
.secretId(secretName)
.versionStage(VERSION_STAGE)
.build();
}
public String getSecret(String secretName) {
return secretsManagerClient.getSecretValue(generateGetSecretValueRequest(secretName)).secretString();
}
}

unit testing in spring boot giving error when exception thrown in service

So, I have this unit test that I need to run.
#MockBean
private AppServiceImpl appService;
#Test
public void shouldThrowExceptionWhenAppIdIsNull() throws Exception {
File inputFile = this.getJsonFile();
RequestDto requestDto = objectMapper.readValue(inputFile.getAbsoluteFile(),
RequestDto.class);
AppData appData = requestDto.getAppData();
appData.setAppId(null);
requestDto.setAppData(appData);
when(appService.getUrl(requestDto, "header")).thenThrow(new RequestNotValidException());
String payload = objectMapper.writeValueAsString(requestDto);
this.mockMvc.perform(post(Base_URL + "app/requesturl")
.contentType(contentType).content(payload).header(this.Header, "header"))
.andExpect(status().is4xxClientError());
}
Interface for service:
SO when I run this test, it throws an exception and doesn't actually assert the test here.
I have added #ResponseStatus(HttpStatus.BAD_REQUEST) on top of RequestNotValidException and it extends RunTimeException
And in the second test case, I get empty response. I tried this API vis Postman and I get the response. Everything works fine there.
#Test
public void getCardRegistration() throws Exception {
File inputFile = this.getJsonFile();
RequestDto requestDto = objectMapper.readValue(inputFile.getAbsoluteFile(), RequestDto.class);
ResponseDto responseDto = new ResponseDto();
responseDto.setURL(AuthUtils.randomStringToken(35));
given(appService.getRegistrationUrl(requestDto, "header")).willReturn(responseDto);
String payload = objectMapper.writeValueAsString(requestDto);
MvcResult mvcResult = this.mockMvc.perform(post(Base_URL + "app/requesturl")
.contentType(contentType).content(payload).header(this.Header, "header"))
.andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString();
}
Controller content:
#Autowired
IAppService appService;
#RequestMapping(value = "/app/requesturl", method = RequestMethod.POST)
public ResponseDto getCardsRegistration(#RequestBody #Valid RequestDto requestDto, #RequestHeader(value="X-App-Name", required = true) String header) throws RequestNotValidException, JsonProcessingException {
log.info("Request received in controller: "+ mapper.writeValueAsString(cardRegistrationRequestDto));
log.info("Header value: "+ header);
return this.appService.getRegistrationUrl(requestDto, header);
}
Test Class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class AppRestControllerTest {
protected String Base_URL = "/app";
protected String Header = "X-App-Name";
protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#Autowired
protected MockMvc mockMvc;
protected ObjectMapper objectMapper = new ObjectMapper();
#MockBean
private AppServiceImpl appService;
#Mock
private AppRegistrationRepository appRegistrationRepository;
#Before
public void setUp() throws Exception {
MapperFacade mapperFacade = new DefaultMapperFactory.Builder().build().getMapperFacade();
appService = new AppServiceImpl(appRegistrationRepository, mapperFacade);
}
What did I miss here?
Try to use
#RunWith(SpringRunner.class)
#WebMvcTest(YourController.class)
public class AppRestControllerTest {
Or
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
#AutoConfigureMockMvc
public class AppRestControllerTest {
In your tests

Spring Boot & OAuth2: Getting ResourceAccessException: I/O error on POST request for "http://localhost:5555/oauth/token": Connection refused: connect

based on this example https://github.com/royclarkson/spring-rest-service-oauth I have modified the source code adding repositories and other rest controllers. The resource and authorization servers are running/configured in the same application.
I have added an endpoint "/api/login" (POST) with request body:
{"usernamme":"foo", "password":"bar"}
I want to get an access token for this user in the service called by the controller. This implementation looks as follows:
Controller
public ResponseEntity<OAuth2AccessToken> login(#RequestBody #Valid final LoginCommand credentials)
throws LoginFailedException, UnexpectedErrorException {
try {
final OAuth2AccessToken token = securityService.authenticate(credentials.getUsername(), credentials.getPassword());
return ResponseEntity.ok(token);
} catch (final InvalidGrantException badCredentialExeption) {
throw new LoginFailedException(badCredentialExeption.getMessage());
} catch (final Exception e) {
throw new UnexpectedErrorException(e.getMessage());
}
}
Service
#Autowired
#Qualifier("OAuth2RestOperations")
private OAuth2RestOperations client;
#Override
public OAuth2AccessToken authenticate(final String username, final String password) {
final ResourceOwnerPasswordResourceDetails resourceDetails = (ResourceOwnerPasswordResourceDetails) client.getResource();
resourceDetails.setUsername(username);
resourceDetails.setPassword(password);
return client.getAccessToken();
}
Rest client config
#Configuration
#Import({ OauthProperties2.class })
#EnableOAuth2Client
public class RestClientConfig {
#Autowired
private OauthProperties2 oauth;
#Bean(name = "OAuth2RestOperations")
public OAuth2RestOperations restTemplate(final OAuth2ClientContext oauth2ClientContext) {
return new OAuth2RestTemplate(resource(), oauth2ClientContext);
}
#Bean
public OAuth2ProtectedResourceDetails resource() {
final ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(oauth.getClient().getAccessTokenUri());
resource.setClientId(oauth.getClient().getClientId());
resource.setClientSecret(oauth.getClient().getClientSecret());
resource.setGrantType(oauth.getClient().getGrantType());
resource.setScope(oauth.getClient().getScope());
return resource;
}
}
The Test
public class SecurityApiControllerTest extends TestBase {
#InjectMocks
private SecurityApiController controller;
#Test
public void loginOK() throws Exception {
final String credentials = FileUtils.readContent("requests/loginOK.json");
// #formatter:off
mvc.perform(post("/api/login")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(credentials))
.andExpect(status().isOk());
// #formatter:on
}
}
TestBase
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Application.class)
public class TestBase {
#Autowired
protected WebApplicationContext context;
#Autowired
protected FilterChainProxy springSecurityFilterChain;
protected MockMvc mvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.webAppContextSetup(context).addFilter(springSecurityFilterChain).build();
}
}
When running the application, I can call the endpoint for example with POSTMAN. During the test I get a connection refused as described in question header. I have tried to figure out the reason why the test is not working. Can anybody give me a hint to solve this issue?
It is so weird. It works since I have changed the TestBase class to:
#RunWith(SpringJUnit4ClassRunner.class)
// #WebAppConfiguration
#ContextConfiguration(classes = Application.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)

Categories