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());
}
Related
I have following Java configuration class which I need to unit test using JUnit:
public class Config {
private static final String AMQ_CONNECTION_URL_TEMPLATE = "failover:(%s)";
private final String awsAmqUrl;
public Config(String url, Optional<String> amqConnectionOptions, PropertiesManager propertiesManager) {
String urlParameter = propertiesManager.getStringParameter(url);
this.awsAmqUrl = constructAmqConnectionString(urlParameter, amqConnectionOptions);
}
private String constructAmqConnectionString(String urlParameter, Optional<String> connectionOptions) {
if (connectionOptions.isPresent()) {
urlParameter = Stream.of(urlParameter.split(","))
.map(url -> url + "?" + connectionOptions.get())
.collect(Collectors.joining(","));
}
return String.format(AMQ_CONNECTION_URL_TEMPLATE, urlParameter);
}
public ConnectionFactory getConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(awsAmqUrl);
return connectionFactory;
}
}
I am struggling to find an optimal solution for constructAmqConnectionString method unit testing as it's marked as private.
There are 3 scenarios I am trying to cover:
urlParameter - comprises comma separated URLs (url1,url2),
connectionOptions is not empty;
urlParameter - comprises comma
separated URLs (url1,url2), connectionOptions is empty;
urlParameter - comprises single URL (url1), connectionOptions is
not empty.
Current solution is to add a getter into Config class for awsAmqUrl field so that logic of constructor's call can be verified/tested:
public String getAwsAmqUrl() {
return this.awsAmqUrl;
}
Tests itself have following logic:
#Test
public void verifyConstructorWithoutMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.empty();
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1,url2)"),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s,url2?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsSingleBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
Adding a getter just for Unit testing purposes doesn't feel the right thing to do as it's breaking encapsulation.
Is there a better way to approach testing in such scenario?
The only place that your class uses awsAmqUrl is in the getConnectionFactory method. So it looks like this is the method you'll have to use, to make sure the value of awsAmqUrl is correct. So instead of having a getter for awsAmqUrl, use something like
String storedUrl = objectUnderTest.getConnectionFactory().getBrokerUrl();
and then you can make assertions on that URL.
Sure, it makes your test dependent on the behaviour of ActiveMQConnectionFactory - but that's OK, since your class is tightly coupled to that particular class anyway.
#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 have a method in the service class that uses an external wrapper to call the slack api. The wrapper I'm using is this one if it makes any difference. This is how I'm using the wrapper,
//This is the method in my service class
public String sendMess(SlackObj obj) {
//SlackObj contains the channel url, channel name and the message
//build the payload from the SlackObj
//Slack is the name of the wrapper's class that I'm using
Slack slack = Slack.getInstance();
//slack.send is the method that sends the message to slack
WebhookResponse res = slack.send(url, payload);
//other logic
}
//This is what I've tried
#Test
public void slackSendMessageTest(){
//build the slack obj and payload
//build the mock WebhookResponse
Slack slackMock = mock(Slack.class)
when(slackMock.send(channelUrl, payload)).thenReturn(mockWebHookRes);
assertEquals("SUCCESS", testService.sendMessage(testSlackObj);
}
I am trying to write some tests for this method, so my question is, how would i test it without having the message sent every time I run the test? I believe the cause of this is because slack itself is not mocked and I have no idea as how to inject the mock into the mocked service class.
I am open to refactoring the service class if it helps with the testing. Any suggestions and recommendation is appreciated. Thanks.
You are going to have to find a way to mock Slack, which appears to be a singleton, unfortunately.
Here's what I would do:
1) Make Slack available as a bean that can be autowired:
#Configuration
public class SlackConfiguration {
#Bean
public Slack slack() {
return Slack.getInstance();
}
}
2) Change your class to take an injected Slack:
Note that I am totally guessing on the name here, as you just show the method. You would inject the Slack object you turned into a #Bean above, and not use Slack.getInstance() directly anywhere else.
#Component
public class SlackService {
private final Slack slack;
#Autowired
public SlackService(final Slack slack) {
this.slack = slack;
}
public String sendMessage(final Object message) {
final WebhookResponse res = slack.send(url, payload);
// etc
}
}
3) Mock the Slack object and pass it to your SlackService in test:
This allows you to mock out the implementation of Slack, so you can alter its behavior. I won't go into mocking in detail.
public class SlacServiceTest {
private final Slack slack = mock(Slack.class);
private final SlackService serviceUnderTest = new SlackService(slack);
#Test
public void testSomething() {
// TODO: set mock responses here
// Given... when... then...
}
}
i am building a http API client that needs to call out to a specific endpoint like so:
public class MyApiClient {
private static final String ENDPOINT ="http://myapi....";
}
Here the endpoint won't change so its constant. However, I want to be able to override this for testing so that I can test against a mock http server for example.
Whats the best way to do this? Is it just to make it an instance variable and provide it with a starting value:
private String endpoint = ="http://myapi....";
public void setEndpoint(String endpoint){
...
}
Well, there are of course many solutions to this and one way of doing it is to use a system property with a default value:
private static final String DEFAULT_ENDPOINT = "http://myapi....";
private static final String ENDPOINT =
System.getProperty("my.endpoint", DEFAULT_ENDPOINT);
This way you get a configurable way of solving your problem. If you need even more flexibility when initializing your static constants you could also use a static initializer:
private static final String ENDPOINT;
static {
// do initialization here but do not throw any exceptions (bad practice)
// you can e.g. read from files etc...
// Then assign your constant...
ENDPOINT =
}
System properties are passed on the command line as -D parameters e.g:
java -Dmy.endpoint=http://...
But in my opinion, an even better approach is to actually inject the value to the class that is using it:
public class ClassThatIsUsingTheConfig {
private final String endpoint;
public ClassThatIsUsingTheConfig(final String endpoint) {
this.endpoint = endpoint;
}
public void someMethod() {
// use endpoint
}
}
And then, make the selection of which endpoint to use in the caller class. From a test case, this will be very easy to mock.
public class MyTest {
#Test
public void testMethod() {
ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(TEST_ENDPOINT);
var.someMethod();
}
}
public class MyProdClass {
public void prodMethod() {
ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(PROD_ENDPOINT);
var.someMethod();
}
}
You can read more about dependency injection here.
On a side note, if you are using some kind of framework for managing dependencies such as Spring Framework or CDI it is common to be able to inject properties and constants in various ways (e.g. based on which environment that is currently running). An example, when using Spring Framework you can declare all your constants in a property file and inject the property using annotations:
#Autowired
public ClassWhoIsUsingTheConfig(#Value("my.endoint") final String endpoint) {
this.endpoint = endpoint;
}
The property file for prod could be along the lines of:
my.endpoint=http://prodserver...
wheras the property file for test would look like this:
my.endpoint=http://testserver...
The approach of using a Dependency Injection engine allows for a very flexible way of handling external constants, paths, resources etc and simplifies your life when it comes to testing the code.
I am currently writing tests for some REST-full Services I wrote. The services I am testing are written in Java and use MongoDb/Morphia. The tests call on the services, some of which in turn write to a test collection. I need to clean up after the tests and delete the data I injected. What is the best way to go about this?
Here is an example of one of my simple services:
package org.haib.myerslab.services;
#Path("/database")
public class DatabaseService {
#Inject
private Datastore ds;
#Path("/genre/")
#POST
#Produces("application/json")
public GenreDTO postFromGenreDTO(#Context UriInfo uri, GenreDTO form) throws ParseException {
Genre myNewGenre = DtoToDomainMapper.gerneFromGenreDTO(form);
myNewGenre.setId(form.getId());
ds.save(myNewGenre);
return new GenreDTO(myNewGenre);
}
}
And here is an example of my Arquillian test:
#RunWith(Arquillian.class)
public class GeneTest {
private static String myId = "myGenreId";
private static String myGenre = "myGenre";
private static String myGenreInfo = "myGenreInfo";
#Deployment
public static WebArchive getDeployment() {
return TestHelper.getDeployment();
}
#Test
#RunAsClient
#InSequence(1)
public void canPostGenre(#ArquillianResource URL baseURL) throws Exception {
GenreDTO newGenre = new GenreDTO();
newGenre.setGenre(myGenre);
newGenre.setGenreInfo(myGenreInfo);
newGenre.setId(myId);
String url = baseURL.toURI().resolve("/database/genre/").toString();
JsonNode rootNode = TestHelper.postUrl(url, newGene);
assertEquals(myGenre, rootNode.get("genre").asText());
assertEquals(myGenreInfo, rootNode.get("genreInfo").asText());
assertEquals(myId, rootNode.get("id").asText());
}
}
Where the getDeployment function looks like this:
public static WebArchive getDeployment() {
File[] depend = Maven.resolver().loadPomFromFile("pom.xml").importRuntimeDependencies().resolve().withTransitivity().asFile();
WebArchive war = ShrinkWrap.create(WebArchive.class).addClass(TestHelper.class)
.addClass(Genre.class).addClass(Application.class).addPackage("org/haib/myerslab")
.addPackage("org/haib/myerslab/database").addPackage("org/haib/myerslab/genre")
.addPackage("org/haib/myerslab/dto").addPackage("org/haib/myerslab/dto/genre")
.addAsLibraries(depend).addAsWebInfResource("jboss-deployment-structure.xml")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml").setWebXML("test-web.xml");
return war;
}
So where I am lost is, what is the best way to Inject the database in an #After, and clear our the Genre Class I posted into it so that my next test doesn't have it there.
How should I do this? Is there another way?
Take a look at nosql-unit. It provides annotations and rules that help you with seeding datasets, comparing expectations and cleaning up MongoDB.
To get your MongoDB into a pristine state before executing a test, you can simply use the following Annotation with the `CLEAN_INSERT´ :
#UsingDataSet(locations="my_data_set.json", loadStrategy=LoadStrategyEnum.CLEAN_INSERT)
public void canPostGenre() { ...}
If you need the behavior around the integration testing lifecycle with MongoDB to be more powerful, you can also roll your own based on the ideas of nosql-unit. Also make sure to check out Junit Rules.