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.
Related
I have this simple OrderResource class that I would like to create unit tests for. I'm qusing Quarkus with Hibernate and Panache. What I don't understand is how do i get the function getOrders in my test? How can I mock the response without running this through a live system? I've currently added the quarkus-panache-mock dependency but i can't figure out how to mock the resource
Should the return values come from some service class? and then be injected into the test?
#Entity
public class Order extends PanacheEntity {
public Long id;
public String summary;
public String description;
}
#Path("orders")
public class OrderResource {
#GET
#Path("orders")
public String getAllOrders() {
return Order.listAll();
}
#POST
#Transactional
public Response create(Order order) {
order.persist();
return Response.created(URI.create("/orders/" + order.id)).build();
}
}
I tried creating a test like this:
#Test
public void testCreateOrder() {
OrderResource orderResource = new OrderResource();
System.out.println(orderResource.getAllOrders().get(0).name); //uses the live system
}
But this is not a unit test but rather an integration test since it takes data from the real database.
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());
}
Consider the following field and method from a class i need to test.
private final static String pathToUUID = "path/to/my/file.txt";
public String getUuid () throws Exception {
return new String(Files.readAllBytes(Paths.get(pathToUUID)));;
}
The UUID is stored in a file that is created on the application's first run. A file.txt exists in the location indicated by pathToUUID. I am trying (and struggling) to write a unit test for this method.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Files.class})
public class MyTest {
private final String expected = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
#Test
public void testGetUuid() throws Exception {
UUIDGetter getter = new UUIDGetter();
PowerMockito.mockStatic(Files.class);
when(Files.readAllBytes(any(Path.class)).thenReturn(expected.getBytes());
String retrieved = getter.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Unfortunately when().thenReturn() is not called during testing and the test performs as an integration test, reading the file from the file system and returning its value, rather simply than the mock value i expect. However, if i spoof a call to Files.readAllBytes() in the test method and echo the result to the console, the expected value displays.
So, how can i get my method under test to properly function with the PowerMock when()-thenReturn() pattern?
For anyone facing a similar problem, i solved this by making the following changes to my test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({UUIDStasher.class})
public class TestUUIDStasher {
private final String expectedUUID = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
Path spoofPath = Paths.get("C:\\DIRECTORY");
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
when(Paths.get(any(String.class))).thenReturn(spoofPath);
when(Files.readAllBytes(any(Path.class))).thenReturn(expectedUUID.getBytes());
}
#Test
public void testGetUUID() throws Exception {
UUIDStasher stasher = new UUIDStasher();
String retrieved = stasher.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Your class that you need to test is written in a bad way. The path shouldn't be hard coded - make it parametrizable - for example inject the path via the constructor. Then, in your integration tests just inject the path to your test resources and you're ready to go. No PowerMock, no hacks - simple constructor injection.
JDK classes are hard to deal with when using PowerMock. Here's what I would do in your case:
Refactor UUIDGetter to add a constructor for testing purposes that accepts the path to the "uuid" file:
package so37059406;
import java.nio.file.Files;
import java.nio.file.Paths;
public class UUIDGetter {
private final static String PATH_TO_UUID = "path/to/my/file.txt";
private final String path;
public UUIDGetter() {
this(PATH_TO_UUID);
}
// for testing purposes
protected UUIDGetter(final String path) {
this.path = path;
}
public String getUuid() throws Exception {
return new String(Files.readAllBytes(Paths.get(this.path)));
}
}
then test it like this:
package so37059406;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UUIDGetterTest {
#Test
public void testGetUuid() throws Exception {
final UUIDGetter getter = new UUIDGetter(getClass().getClassLoader().getResource("so37059406/uuid.txt").getPath());
assertEquals("19dcd640-0da7-4b1a-9048-1575ee9c5e39", getter.getUuid());
}
}
With a resource file (in test resources folder) named "so37059406/uuid.txt" and containing (no end-of-line):
19dcd640-0da7-4b1a-9048-1575ee9c5e39
This is IMHO, way better, because:
No powermock: it's a powerful tool but it comes with a price (slower tests, possible tests strange interactions
It's more readable / easy to understand
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.
Is anyone aware of any examples of testing a Java based Play Framework controller by setting mock objects?
I am using Spring in my Play project so all my controller methods are not static.
Testing the tradional way, Play shows my controller as having static methods and I just cant see a way of how I can inject mocks into my object
Result result = callAction(
controllers.routes.ref.LoginController.authenticate(),
fakeRequest().withFormUrlEncodedBody(TestUtils.SUCCESSFUL_LOGIN_MAP)
);
I have a number of services that need to be called in the LoginController and I would like to set those up as mocks
Any help is greatly appreciated
Thanks
Damien
I was looking for the solution of the same problem. So far the best result I was able to achieve is this:
public class MyObjectControllerTest{
private final MyObjectDAO dao = mock(MyObjectDAO.class);
private final MyObjectController controller = new MyObjectController(dao);
public static FakeApplication fakeApplication;
#BeforeClass
public static void startApp() {
fakeApplication = Helpers.fakeApplication();
Helpers.start(fakeApplication);
}
#AfterClass
public static void stopApp() {
Helpers.stop(fakeApplication);
}
#Test(expected = NotFoundException.class)
public void testFailWithUnknownMyObjectKey() throws Throwable {
when(dao.getByKey(any(UUID.class), any(UUID.class), any(Boolean.class))).thenReturn(null);
controller.get(CassandraUUIDs.timeBased());
}
#Test
public void testGetSuccess(){
MyObject deletedObject = MyObjectTestGenerator.generateMyObject();
deletedObject.setDeleted(true);
when(dao.getByKey(any(UUID.class), any(UUID.class), any(Boolean.class))).thenReturn(deletedObject);
try {
Result result = controller.get(CassandraUUIDs.timeBased());
assertThat(status(result)).isEqualTo(Http.Status.GONE);
assertThat(contentType(result)).isEqualTo(Http.MimeTypes.JSON);
assertThat(contentAsString(result)).isEqualTo(ErrorMsg.OBJECT_DELETED.toJson().toString());
} catch (MyObjectsException e) {
e.printStackTrace();
fail("Failed to send MyObject.get request.");
}
}
}
What I do here is instantiate an instance of the controller class and pass mocked DAO instance. Please note that I don't use static controller methods in my code as well.
One issue with this workaround I found so far is that Action (I have custom one) is not working. But Action can (and probably must) be tested separately.