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) :
- Start transaction t1,
- Execute setup, result is fetched from cache.
- Execute test.
- Start transaction t2 for controller execution GET("/")
- Result is fetched from database but since t1 hasn't been commmited, it isn't displayed.
- Close transaction t2 and commit t1!
- 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
精彩评论