2

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.

mkarki
  • 59
  • 1
  • 2
  • 10

4 Answers4

2

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

Matthew
  • 10,361
  • 5
  • 42
  • 54
  • Thank you for your response. I did have extension and made variable non-private too, but have the same issues. – mkarki May 28 '21 at 15:19
  • The NPE implies your Mock objects aren't being setup / injected in properly - which is odd. You could try it without `lenient()` or you could try using the "other" when syntax: Try using the "other" mockito syntax: `doReturn(secretValueResult).when(secretsClient).getSecretValue(secretValueRequest)` - see if either of those help? – Matthew May 28 '21 at 15:27
  • Yes, right. I couldn't even figure it out why its not been injected. I already tried with doReturn and removing the lenient(), its an same error that the value passed at when is null. – mkarki May 28 '21 at 15:49
1

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);
Todd
  • 11
  • 2
0

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);
    ...
}
henry sneed
  • 356
  • 4
  • 14
0

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();
    }
}
ohrlando
  • 106
  • 11