PlayFramework - Running functional test with json rendering - java

I'm trying to run this functional test
public class JsonRenderTest extends FunctionalTest {
#Before
public void setup() {
Fixtures.deleteDatabase();
Fixtures.loadModels("data.yml");
}
#Test
public void testThatJsonRenderingWorks() {
Response response = GET("/recipe/1");
assertIsOk(response);
}
}
The action answering this call is this
public static void showRecipe(Long id){
Recipe recipe = Recipe.findById(id);
notFoundIfNull(recipe);
renderJSON(recipe);
}
When I run the test in firefox using the TestRunner at http://localhost:8080/#tests
I get this error message:
Failure, Response status expected:<200> but was:<404>
Now if I run this url http://localhost:8080/recipe/1 in a browser, I get the json responce I'm expecting wich is a json representation of my recipe object.
There is of course a recipe in the database with id 1.
Now here is my question. Why is the test failing when the browser is not. I tryed this in Chrome, IE and FF with the same result.
Any pointers would be much appreciated.
Thanks
-Alain

Thanks eveyone.
Ok I found the answer.
It appears that my test was running before the fixture data was fully loaded.
I was running my tests against a local MySql database.
When I removed the call
Fixtures.deleteDatabase();
The test was running fine.
To fix the problem I am now running my test against a mem database with this in the application.conf file
%test.db.url=jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0

You can use a helper method to add content type to your request
public Request jsonRequest(Request request) {
request.contentType = "application/json";
request.format = "json";
return request;
}
Then your test become
#Test
public void testThatJsonRenderingWorks() {
Response response = GET(jsonRequest(newRequest)), "/recipe/1");
assertIsOk(response);
}
}
You can also you WS classes to do real http tests insted.

Related

Play! Framework Functional Test ghost data

Hoping someone else is having the same issue as me, or has other ideas.
I'm currently running Play 1.4.x (not by choice), but also working on upgrading to play 1.5.x, though I verified the same issue happens on both versions.
I created a simple Functional Test that loads data via fixtures
My fixture for loading test data is like so
data.yml
User(testUser):
name: blah
AccessToken(accessToken):
user: testUser
token: foo
Data(testData):
user: testUser
...
I've created a controller to do something with the data like this, that has middleware for authentication check. The routes file will map something like /foo to BasicController.test
public class BasicController extends Controller{
#Before
public void doAuth(){
String token = "foo"; // Get token somehow from header
AccessToken token = AccessToken.find("token = ?", token).first(); // returns null;
// do something with the token
if(token == null){
//return 401
}
//continue to test()
}
public void test(){
User user = //assured to be logged-in user
... // other stuff not important
}
}
Finally I have my functional test like so:
public class BasicControllerTest extends FunctionalTest{
#org.junit.Before
public void loadFixtures(){
Fixtures.loadModels("data.yml");
}
#Test
public void doTest(){
Http.Request request = newRequest()
request.headers.put(...); // Add auth token to header
Http.Response response = GET(request, "/foo");
assertIsOk(response);
}
}
Now, the problem I'm running into, is that I can verify the token is still visible in the headers, but running AccessToken token = AccessToken.find("token = ?", token).first(); returns null
I verified in the functional test, before calling the GET method that the accessToken and user were created successfully from loading the fixtures. I can see the data in my, H2 in-memory database, through plays new DBBrowser Plugin in 1.5.x. But for some reason the data is not returned in the controller method.
Things I've tried
Ensuring that the fixtures are loaded only once so there is no race condition where data is cleared while reading it.
Using multiple ways of querying the database via nativeQuery jpql/hql query language and through plays native way of querying data.
Testing on different versions of play
Any help would be very much appreciated!
This issue happens on functional tests, because JPA transactions must be encapsulated in a job to ensure that the result of the transaction is visible in your method. Otherwise, since the whole functional test is run inside a transaction, the result will only visible at the end of the test (see how to setup database/fixture for functional tests in playframework for a similar case).
So you may try this:
#Test
public void doTest() {
...
AccessToken token = new Job<AccessToken>() {
#Override
public User doJobWithResult() throws Exception {
return AccessToken.find("token = ?", tokenId).first();
}
}.now().get();
....
}
Hoping it works !
I think I had a similar issue, maybe this helps someone.
There is one transaction for the functional test and a different transaction for the controller. Changes made in the test will only become visible by any further transaction if those changes were committed.
One can achieve this by closing and re-opening the transaction in the functional test like so.
// Load / Persist date here
JPA.em().getTransaction().commit(); // commit and close the transaction
JPA.em().getTransaction().begin(); // reopen (if you need it)
Now the data should be returned in the controller method.
So your test would look like this:
public class BasicControllerTest extends FunctionalTest{
#org.junit.Before
public void loadFixtures(){
Fixtures.loadModels("data.yml");
JPA.em().getTransaction().commit();
// JPA.em().getTransaction().begin(); reopen (if you need it)
}
#Test
public void doTest(){
Http.Request request = newRequest()
request.headers.put(...); // Add auth token to header
Http.Response response = GET(request, "/foo");
assertIsOk(response);
}
}
I did never try this with fixtures. But i would assume they run in the same transaction.

JUnit RestControllerTest for #PutMapping throws InvocationTargetException

I'm building a microservice using Spring Boot. I wrote an API with GET-, POST-, PUT-, DELETE- Methods, run the application and tested it using Postman - everything's working fine...
But testing the PUT-Method fails with
java.lang.AssertionError: Status expected:<204> but was:<400>
Running the test in debug-mode and stepping throw throws an InvocationTargetException:
My RestController-Methods looks like this:
#PutMapping(value = "/{id}")
public ResponseEntity updateSongById(#PathVariable("id") Integer id, #RequestBody #Validated
SongDto songDto) {
// TODO Add authorization
SongDto song = songService.getSongById(id);
if (song == null)
return new ResponseEntity(HttpStatus.BAD_REQUEST);
return new ResponseEntity(songService.updateSong(id, songDto), HttpStatus.NO_CONTENT);
}
songService.getSongById(id):
#Override
public SongDto getSongById(Integer id) {
return songMapper.songToSongDto(songRepository.findById(id)
.orElseThrow(NotFoundException::new));
}
The SongRepository is just a simple Interface which extends JpaRepository<Song, Integer>.
My failing test looks like this:
#Test
void updateSongById_success() throws Exception {
when(songService.updateSong(anyInt(), any(SongDto.class))).thenReturn(getValidSongDto());
String songDtoJson = objectMapper.writeValueAsString(getValidSongDto());
mockMvc.perform(put("/rest/v1/songs/1")
.content(songDtoJson)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
}
And getValidSongDto() just provides a Dto used in my tests:
private SongDto getValidSongDto() {
return SongDto.builder()
.id(1)
.title("TestSongValid")
.label("TestLabelValid")
.genre("TestGenreValid")
.artist("TestArtistValid")
.released(1000)
.build();
}
I really don't understand at the moment, what I did wrong to make this test fail and also couldn't find anything in the internet which helped me solving this problem, so far. So, therefor I'd be very thankful, if anybody could tell me what's wrong here and how to solve this issue.
Thank you very much!!
You need to return the value for songService.getSongById as shown below
#Test
void updateSongById_success() throws Exception {
when(songService.getSongById(Mockito.any())).thenReturn(getValidSongDto());
when(songService.updateSong(anyInt(), any(SongDto.class))).thenReturn(getValidSongDto());
String songDtoJson = objectMapper.writeValueAsString(getValidSongDto());
mockMvc.perform(put("/rest/v1/songs/1")
.content(songDtoJson)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
}

Dropwizard 1.0 Integration Testing: Hitting an external API

I'm trying to figure out how to integrate an external API and run every integration test against it. I've been reading and looking at:
https://github.com/dropwizard/dropwizard/blob/master/dropwizard-example/src/test/java/com/example/helloworld/IntegrationTest.java
https://github.com/dropwizard/dropwizard/blob/master/docs/source/manual/testing.rst
but it looks like these are examples of testing local endpoints and not external ones. I would like to be able to test my api calls with JUnit tests. Currently I'm having to start up and run my app to make sure they're working.
This is the direction I'm currently exploring:
private Client client;
#Before
public void setUp() throws Exception {
client = ClientBuilder.newClient();
}
#After
public void tearDown() throws Exception {
client.close();
}
#Test
public void testHitApi() throws Exception {
client.target("https://api.github.com/users/" + getUser() + "/repos");
}
Any help would be much appreciated, thanks!
You need to make the api call to hit the endpoint.
doing just :
client.target("https://api.github.com/users/" + getUser() + "/repos")
returns a WebTarget .
you should ideally do something like:
client
.target("https://api.github.com/users/" + getUser() + "/repos")
.request()
.get() ; // for a get call
google for exact post/put/delete calls .
If you mean to run your integration tests against an external api or a separate running instance of your api.
testEnvironment = new Environment("Test environment", Jackson.newObjectMapper(),
null, new MetricRegistry(), null);
ObjectMapper mapper = Jackson.newObjectMapper(new YAMLFactory());
IntegrationTestConfiguration integrationTestConfiguration = mapper.readValue(fixture("integration-testing-config.yml"),
IntegrationTestConfiguration.class);
Instantiate your client as so
exampleClient = new exampleClient(testEnvironment, clientConfiguration);
Hope this helps.

Unit testing a Restlet extractor

I'm looking for some guidance on real unit testing for Restlet components, and specifically extractors. There is plenty of advice on running JUnit to rest entire endpoints, but being picky this is not unit testing, but integration testing. I really don't want to have set up an entire routing system and Spring just to check an extractor against a mock data repository.
The extractor looks like this:
public class CaseQueryExtractor extends Extractor {
protected int beforeHandle(Request request, Response response) {
extractFromQuery("offset", "offset", true);
extractFromQuery("limit", "limit", true);
// Stuff happens...
attributes.put("query", query);
return CONTINUE;
}
}
I'm thinking part of the virtue of Restlets is that its nice routing model ought to make unit testing easy, but I can't figure out what I need to do to actually exercise extractFromQuery and its friends, and all my logic that builds a query object, without mocking so much that I'm losing testing against a realistic web request.
And yes, I am using Spring, but I don't want to have to set the whole context for this -- I'm not integration testing as I haven't actually finished the app yet. I'm happy to inject manually, once I know what I need to make to get this method called.
Here's where I'm at now:
public class CaseQueryExtractorTest {
private class TraceRestlet extends Restlet {
// Does snothing, but prevents warning shouts
}
private CaseQueryExtractor extractor;
#Before
public void initialize() {
Restlet mock = new TraceRestlet();
extractor = new CaseQueryExtractor();
extractor.setNext(mock);
}
#Test
public void testBasicExtraction() {
Reference reference = new Reference();
reference.addQueryParameter("offset", "5");
reference.addQueryParameter("limit", "3");
Request request = new Request(Method.GET, reference);
Response response = extractor.handle(request);
extractor.handle(request, response);
CaseQuery query = (CaseQuery) request.getAttributes().get("query");
assertNotNull(query);
}
}
Which of course fails, as whatever set up I am doing isn't enough to make Restlets able to extract the query parameters.
Any thoughts or pointers?
There is a test module in Restlet that can provide you some hints about unit testing. See https://github.com/restlet/restlet-framework-java/tree/master/modules/org.restlet.test/src/org/restlet/test.
You can have a look at class HeaderTestCase (see https://github.com/restlet/restlet-framework-java/blob/master/modules/org.restlet.test/src/org/restlet/test/HeaderTestCase.java).
For information, if you use attributes from request, your unit test will pass ;-) See below:
public class CaseQueryExtractor extends Extractor {
protected int beforeHandle(Request request, Response response) {
extractFromQuery("offset", "offset", true);
extractFromQuery("limit", "limit", true);
// Stuff happens...
CaseQuery query = new CaseQuery();
Map<String,Object> attributes = request.getAttributes();
attributes.put("query", query);
return CONTINUE;
}
}
I don't know if you want to go further...
Hope it helps you,
Thierry

MockWebServer's takeRequest() method takes long to response or hangs

I'm using MockWebServer library in my Android JUnit tests. I'm testing an SDK that makes calls to a server. So I'm using MockWebServer to override these server URLs and capture what the SDK is sending to make assertions on it.
The problem that I'm running into is that if I try to do server.takeRequest() and assign it to a new RecordedRequest variable, the test hangs up on the second server.takeRequest() and sometimes, even on the first one -- if I run it on an emulator it hangs on the first server.takeRequest() method but if I run it on my physical Android device, it freezes on the second server.takeRequest() method.
public void testSomething() {
final MockWebServer server = new MockWebServer();
try {
server.play();
server.enqueue(new MockResponse().setBody("")
.setResponseCode(HttpURLConnection.HTTP_INTERNAL_ERROR));
server.enqueue(new MockResponse().setBody("")
.setResponseCode(HttpURLConnection.HTTP_OK));
server.enqueue(new MockResponse().setBody("")
.setResponseCode(HttpURLConnection.HTTP_OK));
URL url = server.getUrl("/");
// This internal method overrides some of the hardcoded URLs
// within the SDK that I'm testing.
Util.overrideUrls(url.toString())
// Do some server calls via the SDK utilizing the mock server url.
RecordedRequest requestFor500 = server.takeRequest();
// Do some assertions with 'requestFor500'
// Do some more server calls via the SDK utilizing the mock server url.
/*
* This is the part where the JUnit test hangs or seems to go into
* an infinite loop and never recovers
*/
RecordedRequest requestAfter500Before200 = server.takeRequest();
} catch {
...
}
}
Am I doing something wrong or is this some type of bug with MockWebServer?
Add timeout to MockWebServer so that it does not hang
server.takeRequest(1, TimeUnit.SECONDS);
There seems to be a problem with MockWebServer's dispatch queue, which freezes for some reason when serving responses which are not 200 or 302. I have solved this by providing a custom dispatcher:
MockWebServer server = ...;
final MockResponse response = new MockResponse().setResponseCode(401);
server.setDispatcher(new Dispatcher() {
#Override
public MockResponse dispatch(RecordedRequest request)
throws InterruptedException {
return response; // this could have been more sophisticated
}
});
Tested with MockWebServer 2.0.0

Categories

Resources