开发者

playframework: -- Database/Test Framework/Cache bug

I have fully isolated this problem to a very simple play app I think it has to do with some DB caching, but I can't figure it out

BasicTest.java 
========== 
import org.junit.*; 
import play.test.*; 
import play.Logger; 
import models.*; 
import play.mvc.Http.*; 
public class BasicTest extends FunctionalTest { 
    @Before public void setUp() { 
    Fixtures.deleteDatabase(); 
    Fixtures.loadModels("data.yml"); 
    Logger.debug("countFromSetup=%s",User.count()); 
    } 
    @Test 
    public void test() { 
    Response response= GET("/"); 
    Logger.debug("countFromTest=%s开发者_StackOverflow社区",User.count()); 
    assertIsOk(response); 
    } 
} 

Uncommented Configs 
================ 
%prod.application.mode=prod 
%test.application.mode=dev 
%test.db.url=jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0 
%test.db=mysql:root:xxx@t_db 
%test.jpa.ddl=create 
%test.mail.smtp=mock 
application.mode=dev 
application.name=test 
application.secret=jXKw4HabjhaNvosxgzq39to9BJECtOr39EXrEabsQAZKi7YoWAwQWo3B BFUOQnJw 
attachments.path=data/attachments 
date.format=yyyy-MM-dd 
db=mysql:root:xxx@db 
mail.smtp=mock 
Application.java 
============ 
package controllers; 
import play.*; 
import play.mvc.*; 
import models.*; 
public class Application extends Controller { 
    public static void index() { 
    Logger.debug("countFromIndex=%s",User.count()); 
    render(); 
    } 
} 
>play test 

Output of log after running the BasicTest http://localhost:9000/@tests 
================================================== 
11:54:59,008 DEBUG ~ countFromSetup=1 
11:54:59,021 DEBUG ~ countFromIndex=0 
11:54:59,034 DEBUG ~ countFromTest=1 
point to browser=> http://localhost:9000 
12:25:59,781 DEBUG ~ countFromIndex=1 

What happened to the record during? Response response= GET("/"); This 'bug' almost makes my test cases useless


It has probably something to do with transactions. I've came across a similar case once with Spring/JUnit couple.

Here is the transactionnal execution of the test (I think) :

  1. Start transaction t1,
  2. Execute setup, result is fetched from cache.
  3. Execute test.
  4. Start transaction t2 for controller execution GET("/")
  5. Result is fetched from database but since t1 hasn't been commmited, it isn't displayed.
  6. Close transaction t2 and commit t1!
  7. Close transaction t1 and commit t2!

By the way, that is not really a Functionnal Test. For functionnal tests, you are not supposed to check such data but only http status. Turn to UnitTests for that. When looking at source code of functionnal tests, you can see all the checks implemented are for response/http checking.


I think its the default behavior of JUnit, @Before annotation makes the method run before every test:

When writing tests, it is common to find that several tests need similar objects created before they can run. Annotating a public void method with @Before causes that method to be run before the Test method. The @Before methods of superclasses will be run before those of the current class.

From : http://junit.sourceforge.net/javadoc/org/junit/Before.html

IF you want the setup to be run once you can use @BeforeClass Annotation : http://junit.sourceforge.net/javadoc/org/junit/BeforeClass.html


In PlayFramework, there's n+1 threads for prod and 1 thread for test profile or compile profile. So if you have a dual-core CPU, there's 3 threads if you are running in prod, and one thread if you started the application with "test". Now, one more interesting fact : there'x one Tx per execution. Thus when your application starts, and you launch your very first test, here is what happens :

  • Play starts with one thread.
  • The JUnitRunner starts, the first test myTest gets executed. It's an HTTP connection to the application. The reason why you see 0 is because of the Response GET that is executed before the @Before statement.
  • The @Before gets executed, creates your entries and the result count is accurate in the @Before, because it's done in the same Tx.

So what I suggest is that you either use @BeforeClass, or perform the setup not in a @Before but from a direct call in myTest for the very specific test case with Response.

I assume that if you replace this code

@Test
public void myTest() {
    Response response= GET("/test");
}

with this

@Test
public void myTest() {
    assertEquals(1,User.count());
}

Correct ?

So the reason why you get this is not a bug. It's simply because of this one thread configuration we have for test environment.

Nicolas

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜