#Qualifier not working in unit test, while injecting in constructor - java

I have a application whose unit tests work correctly. I am trying to move the application to use dependency injection instead of using #AutoWired annotation.
After I make the change, some unit tests fail randomly(No changes have been made to the tests, just the dependency injection part of the application has been changed). I am assuming that the system is unable to create the AmazonS3 client correctly when the dependencies are injected.
See screenshot below, I am trying to create a s3ClientAlphaBeta instance but the application is creating a s3ClientGammaProd instance.
What could I be missing?
Abreviated code:
S3Accessor.java:
#Component
public class S3Accessor {
private AmazonS3 s3ClientAlphaBeta;
private AmazonS3 s3ClientGammaProd;
#Inject
public S3Accessor(#Qualifier("s3ClientAlphaBeta") AmazonS3 s3ClientAlphaBeta,
#Qualifier("s3ClientGammaProd") AmazonS3 s3ClientGammaProd) {
this.s3ClientAlphaBeta = s3ClientAlphaBeta;
this.s3ClientGammaProd = s3ClientGammaProd;
}
public String getHtmlContent(final String deviceType, final String s3Key, final String region) {
final String filePath = generateFilePath(deviceType.toLowerCase(Locale.ROOT), s3Key);
AmazonS3 amazonS3 = s3ClientAlphaBeta;
final String regionSpecificLegoBucket = S3BUCKET_NAME_BETA;
final AmazonS3 regionSpecificAmazonS3 = amazonS3;
return this.getHtmlContentFromS3(regionSpecificLegoBucket, filePath, regionSpecificAmazonS3);
}
private String getHtmlContentFromS3(final String bucketName, final String filePath, final AmazonS3 amazonS3) {
String s3HtmlContent = amazonS3.getObjectAsString(bucketName, filePath);
return s3HtmlContent;
}
S3AccessorTest.java:
public class S3AccessorTest {
#InjectMocks
private S3Accessor s3Accessor;
#Spy
#Qualifier("s3ClientAlphaBeta")
private AmazonS3 s3ClientAlphaBeta;
#Spy
#Qualifier("s3ClientGammaProd")
private AmazonS3 s3ClientGammaProd;
private static final String TEST_VALID_STRING_DESKTOP = "<html><body>Some Content For DESKTOP</body></html>";
private static final String TEST_VALID_WRAPPED_STRING_DESKTOP =
PAINTERS_LEGO_OPENING_DIV + TEST_VALID_STRING_DESKTOP + PAINTERS_LEGO_CLOSING_DIV;
private static final String TEST_VALID_S3_KEY = "/test/.1/main";
private static final String SLASH = "/";
static {
AppConfig.destroy();
AppConfig.initialize("TestApp",
"TestGroup",
new String[] { "--root=configuration/", "--domain=test", "--realm=Ramu" });
}
#BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
s3Accessor.setup();
}
#Test
public void getHtmlContentForDesktop() {
// Arrange
when(s3ClientAlphaBeta.getObjectAsString(anyString(), eq(TEST_VALID_S3_KEY + SLASH + DESKTOP_VIEW)))
.thenReturn(TEST_VALID_STRING_DESKTOP);
// Act
final String outputString = s3Accessor.getHtmlContent(DESKTOP, TEST_VALID_S3_KEY, US_ALPHA_BETA_REGION);
// Assert
assertEquals(TEST_VALID_WRAPPED_STRING_DESKTOP, outputString);
}
CustomRootConfig.java:
#Configuration
#ComponentScan("com.service.controller")
#Import({SmokeTestBeanConfiguration.class })
public class CustomRootConfig {
private static final String NA_S3_SESSION_NAME = "S3Content";
private static final int S3_SESSION_REFRESH_TIME = 3600;
private static final String S3BUCKET_ARN_BETA = "arn:aws:iam::471963992020:role/ProdAccessForAccount";
private static final String S3BUCKET_ARN_PROD = "arn:aws:iam::568948772290:role/ProdAccessForAccount";
#Bean(name = "s3ClientAlphaBeta")
#Singleton
public AmazonS3 s3ClientAlphaBeta() {
return AmazonS3ClientBuilder.standard()
.withRegion(Regions.valueOf(US_ALPHA_BETA_REGION))
.withCredentials(new STSAssumeRoleSessionCredentialsProvider
.Builder(S3BUCKET_ARN_BETA, NA_S3_SESSION_NAME)
.withRoleSessionDurationSeconds(S3_SESSION_REFRESH_TIME).build())
.build();
}
#Bean(name = "s3ClientGammaProd")
#Singleton
public AmazonS3 s3ClientGammaProd() {
return AmazonS3ClientBuilder.standard()
.withRegion(Regions.valueOf(US_GAMMA_PROD_REGION))
.withCredentials(new STSAssumeRoleSessionCredentialsProvider
.Builder(S3BUCKET_ARN_PROD, NA_S3_SESSION_NAME)
.withRoleSessionDurationSeconds(S3_SESSION_REFRESH_TIME).build())
.build();
}

I don't think Mockito actually recognizes the #Qualifier annotation from Spring. The reason it worked before is that Mockito was doing property injection, as there was no other way to do it.
After changes, with the constructor in place, Mockito switched from property injection to constructor injection. What's important is that both s3ClientAlphaBeta and s3ClientGammaProd are of the same type (AmazonS3). Apparently, with constructor injection, Mockito isn't doing so well to differentiate the parameters by its name. Possible reason is that the names of the parameters aren't preserved after compilation, so there's no way for Mockito to match them. Instead, it uses the first mock (spy) it finds with a matching type (either s3ClientAlphaBeta or s3ClientGammaProd) and uses it for both parameters to construct an instance of S3Accessor.
To avoid your issue and gain more control over how the tested object is being created, I'd suggest abandoning usage of #InjectMocks and using the constructor directly in the tested class i.e. doing:
s3Accessor = new S3Accessor(s3ClientAlphaBeta, s3ClientGammaProd);
somewhere below MockitoAnnotations.openMocks(this).
You can find more information on how #InjectMocks annotation works here.

Related

Unit Test IBM MQ

I'm new at the MQ topic.
How do I Test/Unit-Test my Java Services, which do actions against a real Queue.
My classes do connections, deletes etc.
Is there any way to test these services without performing actions against the productive Queues?
#Service
public class QueueConnectionService {
private final MQConfigMapping configMapping;
private MQQueueManager queueManager;
#Autowired
public QueueConnectionService(MQConfigMapping configMapping) {
this.configMapping = configMapping;
}
MQQueue connect(String queuePropertyTitle, int openOptions, String queueName) throws MQException {
MQEnvironment.hostname = configMapping.getNamed().get(queuePropertyTitle).getHostname();
MQEnvironment.channel = configMapping.getNamed().get(queuePropertyTitle).getChannel();
MQEnvironment.port = configMapping.getNamed().get(queuePropertyTitle).getPort();
MQEnvironment.userID = configMapping.getNamed().get(queuePropertyTitle).getUser();
MQEnvironment.password = configMapping.getNamed().get(queuePropertyTitle).getPassword();
queueManager = new MQQueueManager(configMapping.getNamed().get(queuePropertyTitle).getQueueManager());
return queueManager.accessQueue(queueName, openOptions);
}
}
This is my QueueConnectionService, but I have no clue how to use this one at local tests.
In order to do unit tests, you will need to mock the IBM MQ classes, using a framework like Mockito.
One issue you'll encounter is that you cannot mock the call to the MQQueueManager constructor, as the new operator cannot be mocked. I would recommend creating a MqQueueManagerFactory service, autowire this service into your QueueConnectionService and mock it in the test.
Your unit test will end up looking like this:
#ExtendWith(MockitoExtension.class)
class QueueConnectionServiceTest {
private static final String TITLE = "Some random string";
private static final String QUEUE_MANAGER = "Actual value does not matter";
private static final String NAME = "Really. Those constants are magic strings.";
private static final int OPTIONS = 42;
#InjectMock
private QueueConnectionService service;
#Mock(answer = RETURNS_DEEP_STUBS)
private MQConfigMapping configMapping;
#Mock
private MqConnectionManagerFactory connectionManagerFactory;
#Mock
private MQConnectionManager connectionManager;
#Mock
private MQQueue queue;
#Test
void should_provide_queue() {
when(configMapping.getNamed().get(TITLE).getQueueManager()).thenReturn(QUEUE_MANAGER);
// repeat above line for each configuration parameter.
// Alternatively, create a config object and:
when(configMapping.getNamed().get(TITLE)).thenReturn(config);
when(connectionManagerFactory.create(QUEUE_MANAGER)).thenReturn(connectionManager);
when(connectionManager.accessQueue(NAME, OPTIONS)).thenReturn(queue);
var actual = service.connect(TITLE, OPTIONS, NAME);
assertSame(actual, queue);
}
}
(This is with JUnit 5 and Mockito. You would have something slightly different with another testing framework or another mocking framework.)
As a side note, creating a queue manager at each connection smells bad. You probably want to create it once in an #PostConstruct method.
You could set up a development queue manager to run unit tests against. See https://developer.ibm.com/learningpaths/ibm-mq-badge/

How to write unit test for URI create in java

Below is the method I'm trying to write unit test using junit 5
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Below is the unit test I'm trying to run
#InjectMocks
private AWSAuthHandler testAWSAuthHandler;
#Test
public void testSetAwsRequestGETParameter() throws Exception {
URI mockedURI = Mockito.mock(URI.class);
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
Below is the stack trace:
java.lang.NullPointerException
at java.base/java.net.URI$Parser.parse(URI.java:3106)
at java.base/java.net.URI.<init>(URI.java:600)
at java.base/java.net.URI.create(URI.java:881)
Can someone please help me with the missing part? Thank you
For setting properties of class that you can't mock you can use Spring Reflection Utils, like that:
ReflectionUtils.setField(field, target, value);
where the field is the name of the field which you want to set ("endpoint" for your case),
target is the mocked class (testAWSAuthHandler for your case)
value is the wanted value
As Sweta Sharma said, you need to initialise AWSAuthHandler with some value for endpoint field. That's why it is better to use constructor injection rather than field one.
Assuming your AWSAuthHandler class look like this (as you didn't provide the code for the whole class):
public class AWSAuthHandler {
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
You can refactor it in the following way:
public class AWSAuthHandler {
private String endpoint;
public AWSAuthHandler(#Value("${proxy.host}") String endpoint) {
this.endpoint = endpoint;
}
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Then you can create tests for this class:
private AWSAuthHandler testAWSAuthHandler;
#BeforeEach
void setUpTests() {
this.testAWSAuthHandler = new AWSAuthHandler("some-endpoint-here");
}
#Test
public void testSetAwsRequestGETParameter() throws Exception {
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
You can read more about Spring #Value annotation here, for example: https://www.baeldung.com/spring-value-annotation

Problems with null pointer using mockito

I'm trying to test a method. And in this method, a new Object is instancied, but I don't want it, otherwise other class will be tested.
How I tell to mockito dont intanciate it?
#Component
#EnableScheduling
public class VerificadorDeNovasAssinaturas {
private DocuSign docuSign;
private ApiClient apiClient;
#Autowired
private DocuSignProperties docuSignProperties;
public EnvelopesInformation verificaNovasAssinaturas() throws Exception {
this.docuSign = new DocuSign(docuSignProperties); // I don't want mockito instanciate DocuSign
this.apiClient = docuSign.getApiClient();
this.apiClient.addDefaultHeader("Authorization", "Bearer " + docuSign.getoAuthToken().getAccessToken());
And my test class:
#SpringBootTest
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class VerificadorDeNovasAssinaturasTest {
#InjectMocks
private VerificadorDeNovasAssinaturas verificador;
private DocuSignProperties docuSignProperties;
private ApiClient apiClient;
private UserInfo userInfo;
private OAuthToken oAuthToken;
#Mock
private DocuSign docuSign;
#Before
public void initialize() throws Exception {
docuSignProperties = new DocuSignProperties();
docuSignProperties.setBaseUrl("https://demo.docusign.net/restapi");
docuSignProperties.setBasePath("/restapi");
setApiClientConfigurations();
when(docuSign.getApiClient()).thenReturn(this.apiClient);
when(docuSign.getoAuthToken()).thenReturn(this.oAuthToken);
...}
private void setApiClientConfigurations() throws Exception {
this.apiClient = new ApiClient(this.docuSignProperties.getBaseUrl());
this.oAuthToken = getOAuth();
... }
#Test
public void testaVerificacaoDeNovasAssinaturas() throws Exception {
EnvelopesInformation results = verificador.verificaNovasAssinaturas();
assertNotNull(results);
}
I don't want mockito instanciate a new DocuSign, because this is not the reason of the test. There is some way do ignore this step?
Well, Mockito can not change something if your code( Code to be tested, Which you intend to) does something, However you can mock it so that it does not create a new object (rather have your "Mocked Object"), so that you can verify something against the expected behavior.
In your code if you change few lines , you can achieve what you want, like -
Create a DocuSignService class and there you create this new object in say some - getDocuSign method. Then your code looks something like below -
#Autowired
private DocuSignService docuSignService ;
this.docuSign = new DocuSign(docuSignProperties); // This is what you have
this.docuSign = this.docuSignService.getDocuSign() ; // This is new code
Now in your test case -
#Mock
DocuSignService docuSignService ;
#Mock
private DocuSign docuSign;
//.
//.
Mockito.when(this.docuSignService.getDocuSign()).thenReturn(docuSign);
Now you have control on this object.
I resolved it using powerMockito.
DocuSign docuSign = PowerMockito.mock(DocuSign.class);
PowerMockito.whenNew(DocuSign.class).withAnyArguments().thenReturn(docuSign);

Test the service in springboot

This is the service I have :
#Service
public class UserInfoService {
#Autowired
private UserInfoServiceClient UserInfoServiceClient; // Call another Rest API
public ResponseEntity<ResponseUserInfoData> sendUserInfo(String UserId) throws RuntimeException {
ResponseUserInfoData responseUserInfoData = new ResponseUserInfoData();
//Get the body from the User service client
UserServiceDTO UserServiceDTO = UserInfoServiceClient.sendResponse(UserId).getBody();
//Set the values of responseUserInfoData
Optional<UserServiceDTO> UserServiceDTOOptional = Optional.ofNullable(UserServiceDTO);
if (UserServiceDTOOptional.isPresent()) {
UserServiceDTOOptional.map(UserServiceDTO::getId).ifPresent(responseUserInfoData::setid);
}
else return ResponseEntity.noContent().build();
}
}
I have to test it. I'm new to JUnit testing. I want to test the following points:
To check if the service return the response entity
To check if the get and set method works
This is what I started?
#RunWith(MockitoJUnitRunner.class)
public class ServiceTests {
#InjectMocks
private UserInfoService UserInfoService;
#Mock
private UserInfoServiceClient UserInfoServiceClient;
#Mock
private UserServiceDTO UserServiceDTO;
#Test
public void shouldReturnUserInfoData() throws IOException{
UserInfoService.sendUserInfo("ABC");
}
}
Any help is appreciated?
Mockito is useful to mock the dependencies of the service so that you can test all the code path in you service. In your case you will want to stub the call to serInfoServiceClient.sendResponse(UserId) and have it return a specific UserServiceDTO for each test case.
The test file looks like it is set up correctly, you only need to mock the method to give you the result you need for the particular test, for example
#RunWith(MockitoJUnitRunner.class)
public class ServiceTests {
#InjectMocks
private UserInfoService UserInfoService;
#Mock
private UserInfoServiceClient UserInfoServiceClient;
#Test
public void shouldReturnUserInfoData() throws IOException{
final String userId = "123";
// The mocked return value should be set up per test scenario
final UserServiceDto dto = new UserServiceDto();
final ResponseEntity<UserServiceDTO> mockedResp = new ResponseEntity<>(dto, HttpStatus.OK);
// set up the mock service to return your response
when(UserInfoServiceClient.sendResponse(userId)).thenReturn(mockedResp);
// Call your service
ResponseEntity<ResponseUserInfoData> resp = UserInfoService.sendUserInfo(userId);
// Test the result
Assert.isNotNull(resp);
}
}
There are also other ways to mock the dependencies using Mockito. I suggest going through the quick start of https://site.mockito.org/

How to use variables loaded from configuration files (acces by fields of static obejct or copy it )?

What is faster (and better) ? :
Load variables to a special
static object and when a variable
from configuration file is needed
get variale from static object's
field.
Copy configuration
variable to a local field when
creating new object which needs a
configuration variable.
I prefer instance level myself, as long as you aren't doing it excessively (i.e. Don't read the configuration every time you instantiate something).
Static configurations will give you heartache. Especially for testing.
The best solution in my mind is to use a framework like Spring (or Guice) to inject configuration type information into your objects.
I'd say use on-demand caching. Look at the MapMaker from guava-collections. If you doesn't want to add additional dependency then I'd prefer option 1.
I would abstract the configuration with an interface and provide strategies like:
public interface Config {
public String getUrl();
public String getName();
}
public class PropertiesConfig implements Config {
private final String url;
private final String name;
public PropertiesConfig(String filepath) {
Properties props = // read properties from file input stream
this.url = props.getProperty("url", "");
this.name = props.getProperty("name", "");
}
// getters from interface
}
public class SpringConfig {
private final String url;
private final String name;
public SpringConfig(String contextPath) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(contextPath);
this.url = (String) ctx.getBean("url");
this.name = (String) ctx.getBean("name");
}
// getters from interface
}
Etc., you could provide a bunch of strategies obviously.
public class Application {
private final Config config;
public Application(Config config) {
this.config = config;
}
public String doWork() {
return Client.url(config.getUrl()).get();
}
}

Categories