Play! Framework Functional Test ghost data

100 views Asked by At

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

  1. Ensuring that the fixtures are loaded only once so there is no race condition where data is cleared while reading it.
  2. Using multiple ways of querying the database via nativeQuery jpql/hql query language and through plays native way of querying data.
  3. Testing on different versions of play

Any help would be very much appreciated!

2

There are 2 answers

3
Tom On

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 !

0
Emanuel Gitterle On

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.