Reputation: 59
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.
Upvotes: 2
Views: 11809
Reputation: 136
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();
}
}
Upvotes: 0
Reputation: 526
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.
@Configuration
public class Config {
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public AWSSecretsManager awsSecretsManager(String region) {
return AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
}
}
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;
}
...
}
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);
...
}
Upvotes: 0
Reputation: 11
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);
Upvotes: 1
Reputation: 11347
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
Upvotes: 2