Basic Ebean model testing
When you want to execute some tests that interact with your Models, you need to use a Play FakeApplication
. This is described in the Java section of the Play 2.0 documentation.
Here is how the code looks like :
As you can see, there is a lot of boilerplate code that we would like to avoid.
The easiest solution is to create a Test base class that will create de FakeApplication using a method annotated with @BeforeClass
Here is how the code could look like for the base class :
You can then code your tests like this :
Database cleaning
To keep my tests really independent of each other, I systematically clean the database before each test.
One solution to achieve that is to create a new FakeApplication
before each test. This can be done by replacing the @BeforeClass
/ @AfterClass
annotations with @Before
/ @After
and removing the static
keyword in the methods’ declarations.
It will work well but unfortunately, this will have a bad impact on the execution time. It can be ok if you only have a few tests, but when the number of tests will grow, it can become problematic. It would be nice to find a more efficient solution.
To avoid creating a FakeApplication
for each test, we could just execute directely some drop table
and create table
SQL scripts. This should be faster.
I found an easy way to do that by reusing the evolution
script generated by the EbeanPlugin class. Here is how we can do this :
Now before each test, you automatically have a database cleaned very quickly !
Here is a sample app illustrating this code : https://github.com/mguillermin/play2-ebean-testing
Note that if you have disabled evolutions, you will probably have to hack a little bit more to generate the create
and drop
scripts directly from Ebean. This is what is done in the EbeanPlugin.onStart()
method.
IDE integration
Once you’ve correctly made your tests work with the FakeApplication
, you might want to launch them directly from your IDE.
Why ?
Launching all your tests is easy with the command line. But when you want to launch a specific test class or method, it’s usually easier to use directly your IDE. You can then precisely select which tests to run, launch them in debug mode,…
The problem
If you try to do this on a test that use an Ebean entity, it will fail. Here is an example of what you will get :
java.lang.IllegalStateException: Class [class play.db.ebean.Model] is
enhanced and [class models.Company] is not - (you can not mix!!)
The problem is that your Model class is not enhanced by Ebean when you don’t launch your test from the Play console.
First solution : javaagent
Reading the Ebean reference guide , I found a way to automatically enhance your classes by using a specific javaagent.
All you have to do is to download Ebean, unzip it and then configure your IDE to add a JVM argument when launching the tests.
Here is how to do that with IntelliJ :
- Go to the ‘Run’ / ‘Edit Configurations’ menu
- Find the JUnit configuration under “Defaults”
- Fill the ‘VM Options’ field with :
-javaagent:/path/to/ebean/ebean-2.7.3-agent.jar
You can then enjoy running tests from your IDE.
Second solution : IDE plugin
If you use IntelliJ, there is an easier solution to enable enhancement automatically on your model classes. You will find in the plugin repository the Ebean weaver plugin that will do exactly what we want.
You just have to install it, restart, check the ‘Build’ / ‘Ebean weaving’ menu and that’s all.
Note that such a plugin exists also for Eclipse.
Conclusion
I hope that with these small tips and tricks, you will be able to enjoy even more discovering Play 2.0.
Thankyou so much! I’d been trying to work out an elegant solution for a few days, but haven’t done much Java SE stuff since 1.4 (just J2ME). I didn’t realise you could get so much out of annotations 😀
It’s also possible to use the DdlGenerator of Ebean for clean the DB
Example = https://gist.github.com/2819920
Hi Matthieu,
Thank you for the solution for IDE integration : it works in Eclipse adding Ebean agent !
I have another problem, perhaps you have informations or a solution … With “inMemoryDatabase()”, my unit tests do not work, I have exceptions :
[error] Test models.ProjectTest.testCreateAndRetrieve failed: Error getting sequence nextval
[error] at com.avaje.ebean.config.dbplatform.SequenceIdGenerator.getMoreIds(SequenceIdGenerator.java:213)
...
[error] ...
[error] Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "SELECT PROJECT_SEQ.NEXTVAL UNION[*] SELECT PROJECT_SEQ.NEXTVAL UNION SELECT PROJECT_SEQ.NEXTVAL ...
Any idea ?
Xavier
Hi Xavier,
I had an idea when I saw your message on the Google Group. See my answer there : https://groups.google.com/forum/#!msg/play-framework/jjZuwIHNMIk/2Pf7mqFA5JsJ
Matthieu
Thanks a lot, very helpful !
Eclipse Ebean plugin works fine.
Great post! Thanks for the very helpful tips.
Question: Did you use YAML for loading test-data for your unit tests by any chance?
I am attempting to use test-data written in YAML for unit testing (and html template testing). However I cannot get this to work for the tests in Eclipse because YAML expects there to be getters/setters for the variables, but as you know Play! supports using just
public
fields and then it implicitly generates getters and setters. This won’t happen when you run a unit test from within Eclipse, and then loading the YAML template will fail..Thought I’d ask, because the only workaround I can think of is writing getters/setters in all my classes and I’d very much like to avoid that.
I’ve just made a quick test of Yaml loading in IntelliJ and it seems to work even without getters/setters (although I had to specify my models class names in the Yaml file). I don’t know if it behaves differently using Eclipse.
Here is the sample code I used : https://gist.github.com/3930185
Thanks! Another 1-0 for IntelliJ over Eclipse
An interesting solution from @bverbeken for removing the boilerplate code of the FakeApplication initialization using a JUnit Runner : http://ostia.be/blog/2012/10/20/play-2-test-runner/
Matthieu,
How do you get IntelliJ and Play to cooperate about the classpath? Do you have them both output to the same target dir?
I cannot get IntelliJ to compile all play sources properly.
And for testdata, I cannot get IntelliJ to out my Yaml file in the target dir. I have to run `play test` in order for my test-file (test/resources/testdata.yml) to be copied to the target/ dir..
This is helpful. Thanks. I had the same problems..
Thanx a lot Matthieu !
Got the same problem on eclipse…
Works fine now with Play 2.1 with the librairy avaje-ebeanorm-agent.jar .
Great Tutorial defenetly solved my problem
I do have a problem though related to eclipse
im following the play tutorial http://www.playframework.com/documentation/2.1.0/JavaGuide4
this test simulate sending a login form with the right login parameters. when running from the console using play test i get http response 303
but when running from eclipse, i get 400
Any idea?
I tried to merge both approaches, https://gist.github.com/nboire/2819920 and https://gist.github.com/mguillermin/3930185, without success. Somehow, if I create a bigger YML (30+ entries) and add more @Test cases, soon or later Play/Ebeans starts to mess with INSERTs, using duplicated IDs and breaking PK constraints rules.
Any clue why?
Anyway, really nice post. Thank you.
I an having a following issue when i m running YABE test in eclipse
Class not found models.ModelTest
java.lang.ClassNotFoundException: models.ModelTest
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClass(RemoteTestRunner.java:693)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClasses(RemoteTestRunner.java:429)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Is it some kind of classpath issue or else. Plz help me out…..
Hey Matthieu,
thanks for the Article. I do encounter a very strage behaviour using a PostgreSQL database. The Downs are apparently not executed so I get an error, that tables already exist when it comes to the Ups. Do you (or anyone around) have an explanation for that?
Grateful for any hint,
Steven
Thank you Matthieu, I had problem with this and now my world brightened.
Thanks a lot again.
I had an issue when using this class where my first evolution had only one up and no downs. I fixed it by replacing these two lines :
createDdl = upsDowns[0];
dropDdl = upsDowns[1];
with
createDdl = upsDowns.length > 0 ? upsDowns[0] : "";
dropDdl = upsDowns.length > 1 ? upsDowns[1] : "";
Is there a way I can disable the DDL logging output?
The DDL logging comes from Ebean directly. From what I’ve seen in the source code (see http://grepcode.com/file/repo1.maven.org/maven2/org.avaje/ebean/2.8.1/com/avaje/ebeaninternal/server/ddl/DdlGenerator.java), it prints everything to `System.out` by default.
Unfortunately, there isn’t any constructor arguments or methods available to override this behavior.
In the EBean docs, they mention turing up/down log levels when they discuss the javaagent approach for enhancing classes (when running tests in an IDE) with this startup option:
-javaagent:d:/jarlib/ebean-agent-2.0.0.jar=debug=3;packages=app.data.*,org.test.model.*
debug of 0 is the lowest, and 10 being highest output. Not sure if it’s possible to somehow use that in this case or not though.
The ebean plugin does not seem to still be in the eclipse marketplace. Can anyone give me the update URL?
The update site url seems to be http://www.avaje.org/eclipseupdate
I’ve tried using the BaseModelTest, but get the following error concerning ebean data source. Have you seen this and do you have any suggestions?
[error] Test models.TicketGroupTest.save failed: javax.persistence.PersistenceException: The default EbeanServer has not been defined? This is normally set via the ebean.datasource.default property. Otherwise it should be registered programatically via registerServer()
[error] at com.avaje.ebean.Ebean$ServerManager.getPrimaryServer(Ebean.java:178)
[error] at com.avaje.ebean.Ebean$ServerManager.access$300(Ebean.java:128)
[error] at com.avaje.ebean.Ebean.save(Ebean.java:453)
[error] at play.db.ebean.Model.save(Model.java:91)
[error] at models.TicketGroupTest.save(TicketGroupTest.java:56)
[error] …
[info] models.TicketGroupTest
Never mind.. my error
Im having this problem and can’t get it to resolve. Any suggestions?
Pingback: How should you test Play framework models when using Ebeans?CopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite query, nodejsquery,
Thanks a lot mate. Works like a charm.
Pingback: Crazy IntelliJ with Play Integration | Leo's Aqu-Blog-Arium
Good information. Lucky me I came across your site by chance (stumbleupon).
I’ve saved it for later!
well done!
Pingback: Play! Framework FakeApplication – What does it actually do? | Solutions for enthusiast and professional programmers
a very good post. Found lots of uncompleted information even in play official site.
Pingback: play framework 2.1 junit test not working from eclipse - QuestionFocus
As you can see, there is a lot of boilerplate code that we would like to avoid