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/
Related
I'm trying to create a unittest for the method below (myHostClient), but I'm having some problems with it:
MyClass.java
import com.abc.def.ServiceBuilder
public class MyClass {
#Value("${materialLocation}")
private String materialValue
private static final SERVICEBUILDER = new ServiceBuilder()
#Bean public MyHostServiceClient myHostClient(
#Value(${qualifier_one}) final String qualiferOne,
#Value(${use_service}) final boolean useService) {
if(useService) {
return SERVICEBUILDER
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
#Bean DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
And here is my latest unsuccessful attempt at writing a unittest for it:
MyClassTest.java
import org.mockito.Mock
import static org.mockito.Mockito.times
public class MyClassTest {
#Mock
private SERVICEBUILDER serviceBuilder;
private MyClass myClass;
private String qualifierOne = "pass"
#BeforeEach
void setUp() {
myClass = new MyClass();
}
#Test
public void test_myHostClient() {
boolean useService = true;
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService); // Error Line1 here
verify(serviceBuilder, times(1));
}
}
I have been trying to mock SERVICEBUILDER and verify that the mocked object is called one time but no luck so far. Right now I'm getting this error:
IllegalArgumentException: Material Name cannot be null
And it points to these lines in my code.
In the Test:
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService);
Which points to this line in the module:
.withCall(new CallerAttach(Caller.retro(defaultStrategy())),
Anyone know how I can fix my unittest or write a working one from scratch?
I would say the design of MyClass is quite wrong because it looks like a Spring configuration but apparently it's not. If it is really supposed to be a configuration then I wouldn't even test it like this because it would rather be an integration test. Of course, even in integration tests you can mock dependencies. But the test itself would run differently and you would have to spin up a suitable Spring context, etc.
So given the above, I would rather make MyClass some sort of MyHostServiceClientFactory with removing all of the Spring annotations and then fix the following problems in your code.
SERVICEBUILDER is hardcoded.
SERVICEBUILDER is static final and its value is hardcoded into MyClass. You will not be able to reassign that field with the mocked version. It can still be final but not static then and it's better to use dependency injection here by passing the value through the MyClass constructor.
SERVICEBUILDER will still be not mocked even if you fix the above.
To really mock SERVICEBUILDER by using the #Mock annotation in the test you should enable Mockito annotations.
If you are using JUnit5 then you should annotate your test class like this:
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
...
}
If you are stuck with JUnit4 then you should use another combination:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
...
}
Once you've done that the SERVICEBUILDER will be mocked but now you will have to configure the behaviour of that mock, like what is going to be returned by the SERVICEBUILDER methods. I can see 4 methods in total, namely remote, withConfig, withCall, and makeClient. You will have to do Mockito's when/thenReturn configurations.
MyClass.materialValue is null.
But even when your mock will be properly configured you will still encounter the original IllegalArgumentException: Material Name cannot be null. This is because MyClass.materialValue will still be null and looks like CredentialsProvider cannot accept that. As I can see, that field is supposed to be injected by Spring using the #Value annotation, but remember this class no longer contains anything from Spring. As in problem 1, you have to pass the value through the MyClass constructor.
Once all of these problems are solved you can introduce a thin Spring configuration like MyHostServiceClientConfiguration (or whatever name suits you) that would serve as a provider of necessary properties/dependencies for MyHostServiceClientFactory (existing MyClass) and then this factory can provide you with a MyHostServiceClient bean through a method like MyHostServiceClientConfiguration#myHostServiceClient annotated with #Bean.
Conceptually your MyHostServiceClientFactory will look like this:
public class MyHostServiceClientFactory {
private final String materialValue;
private final ServiceBuilder serviceBuilder;
public MyHostServiceClientFactory(String materialValue, ServiceBuilder serviceBuilder) {
this.materialValue = materialValue;
this.serviceBuilder = serviceBuilder;
}
public MyHostServiceClient myHostClient(String qualiferOne, boolean useService) {
if(useService) {
return serviceBuilder
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
// can also be injected as a dependency rather than being hardcoded
DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
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.
#Autowired
private Publisher Publisher;
private int Id = 12345;
private BClient bClient = new BClient(Id);
private Map<Integer, Boolean> defaultBooleanValueMap;
private LoadCache<Integer, Boolean> booleanCache = CacheBuilder.newBuilder()
.refreshAfterWrite(refreshRate, TimeUnit.SECONDS)
.build(
new CacheLoader<Integer, Boolean>() {
#Override
public Boolean load(Integer id) throws Exception {
return fetchBooleanValue(id);
}
}
);
private boolean fetchBooleanValue(int id) {
long fetchStart = System.currentTimeMillis();
boolean val = bClient.getBoolean(id, defaultBooleanValueMap.get(id));
publisher.publish(
publisher.getDistributionMetric("fetchtime.bool", System.currentTimeMillis() - fetchStart));
return val;
}
public boolean getBoolean(int id) {
return booleanCache.getUnchecked(id);
}
//Trying to test getBoolean(int id) function. I'm mocking bClient, Publisher. Not sure how to properly test it
// Could anyone help me understand how to test it
//testing with
SomeClass someClass = new SomeClass();
#Mock
Publisher publisher;
#Mock
BClient bClient;
#Test
public void testGetBoolean(){
bClient = new BClient(12345);
Map<Integer,Boolean> defaultBooleanValueMap = null;
defaultBooleanValueMap.put(123, false);
when(bClient.getBoolean(123,
defaultBooleanBregValueMap.get(123))).thenReturn(false);
boolean b = someClass.getBoolean(123);
assertFalse(b);
}
// i'm don't know if i'm doing it right
Are you using Mockito?
It's good practice to not start a field name with a capital (Publisher for instance)
Personally i think it will be better to make all these methods protected instead of private, so that you can test each of them separately.
however this would be an example of a unit test for your code.
You can use Mockito to check if certain method calls are fired the amount of time you expect them to be fired.
I did not include all but you can just add if you need more tests.
Further i recommend to read about Mockito as it has some really powerful unit test tools
#Test
public void testGetBoolean () {
xxx.getBoolean
//the following line can only be done if you spy your service
Mockito.verify(xxx, times(1)).fetchBooleanValue(any());
//this line can be done if you mock bClient
Mockito.verify(bClient , times(1)).getBoolean(any(), any()); //Mockito.any() or you can fill in the real values if you really want.
//this line can be done if you mock Publisher
Mockito.verify(publisher, times(1)).publish(any); //again any or the real value you want to pass
}
I just now saw your unit tests, you can inject the mocks in you class with the following anotatation:
#InjectMocks
SomeClass someClass;
when mocking a class you don't manually have to create it again.
You don't have to mock the Bclient as you already create it with "new Bclient" instead of autowiring it.
I feel the #InjectMocks is not working because you didn't tell Spring that your class is a service component.
#Service
public class SomeClass {
//insert code here
}
I am trying to write unit test case for Retrofit which I'm using to make Google Api call to extract details From google token.
Please help mw to mock this class
Tech Stack
Springboot
(JUnit 4)mockito
i want to write test case for this funcion
This is my function
public String extractGmail(String googleToken) throws IOException {
final Call<GmailDTO> call = googleTokenValidatorAPI.authenticateUsingGmail(googleToken);
final Response<GmailDTO> response = call.execute();
if (response.isSuccessful() && response.body().getHd().equals("nineleaps.com")) {
return response.body().getEmail();
}
throw new Unauthorized("Token Invalid");
}
GmailDTO is used to store response From call.execute()
GmailDTO
public class GmailDTO {
private String atHash;
private String sub;
private boolean emailVerified;
private String kid;
private String iss;
private String typ;
private String givenName;
private String locale;
private String picture;
private String aud;
private String azp;
private String name;
private String hd;
private long exp;
private String familyName;
private long iat;
private String alg;
private String email;
private String jti;
}
This is my Configuration class for Retrofit
#Configuration
public class GmailLoginConfig {
#Bean
public GoogleTokenValidatorAPI googleTokenValidatorAPI() {
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.googleapis.com/")
.addConverterFactory(GsonConverterFactory.create()).build();
return retrofit.create(GoogleTokenValidatorAPI.class);
}
}
and GoogleTokenValidatorAPI class is
#Configuration
public interface GoogleTokenValidatorAPI {
#POST("oauth2/v3/tokeninfo")
Call<GmailDTO> authenticateUsingGmail(#Query("id_token") String token);
}
I don't have a project with Retrofit right now to verify for sure the answer, however you should:
Chose what kind of tests do you want to run: unit test, integration test,etc?
Pick the right tool
Say you want to run the unit test, this means you should treat GoogleTokenValidatorAPI interface as a regular java interface. Unit tests do not start spring and to not use any kind of HttpConnection, so you don't have to do anything with retrofit in particular:
GMailDTO expectedDTO = new GMailDTO(...);
Response<GMailDTO> expectedResponse = Response.success(expectedDTO);
Call<GmailDTO> call = Mockito.mock(Call.class);
Mockito.when(call.execute()).thenReturn(expectedResponse);
GoogleTokenValidatorAPI googleTokenValidatorAPI = Mockito.mock(GoogleTokenValidatorAPI.class);
Mockito.when(googleTokenValidatorAPI.authenticateUsingGmail(googleToken)).thenReturn(call);
....
This test will be fast, and will check your code around the call. You can also simulate a non-successful response like this or even throw an exception in case you want to check how does your code behave if the server is not available
You won't be able to check however that the GmailDTO returned from the server has indeed the same structure as you would expect (think about the hypothetical situation when google has decided to change the API), It won't also check that the annotation that you've put on retrofit interface are indeed valid (path, expected headers, etc.). For that you need to create a test that once in a while indeed makes a call to google (which means technically run it with spring that will create a retrofit client stub proxy in runtime). Technically it won't be a unit test and in general you might not want to run it for each build (its your decision basically). Such a test however has nothing to do with your own code around the call, that's why I've focused primarily on unit test approach.
Finally this worked!!,
#Test
public void extractGmail() throws IOException {
GmailDTO expectedDTO= new GmailDTO();
expectedDTO.setHd("domain");
expectedDTO.setEmail("Email");
Response<GmailDTO> expectedResponse= Response.success(expectedDTO);
Call<GmailDTO> call= mock(Call.class);
when(googleTokenValidatorAPI.authenticateUsingGmail(ArgumentMatchers.anyString())).thenReturn(call);
when(call.execute()).thenReturn(expectedResponse);
GoogleTokenValidatorAPI googleTokenValidatorAPI= mock(GoogleTokenValidatorAPI.class);
userAdminServiceImplements.extractGmail(ArgumentMatchers.anyString());
}
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/