-
Notifications
You must be signed in to change notification settings - Fork 13
Step 12 testing it all
Purpose: in this step we will see how to build automated tests.
cd ~/devel/apps
git clone git://github.com/opensas/play-demo.git
cd play-demo
git checkout origin/12-testing_it_all
Play framework comes with an integrated test suite, and it support three types of tests: unit tests, functional tests and aceptance tests.
Before we begin we'll make sure that our tests run with a specified configuration. Edit application.conf and create the items with the configuration for test mode preffixed with %test., like this:
conf/application.conf
# Testing. Set up a custom configuration for test mode
# ~~~~~
#%test.module.cobertura=${play.path}/modules/cobertura
%test.application.mode=dev
%test.db.url=jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
%test.jpa.ddl=create
%test.mail.smtp=mock
%test.application.langs=en
Here we specified that the language to use in test mode will be english.
Before running your test, you have to make sure you count with a known set of data. Create the following file:
test: data-test.yml
EventType(presentation):
name: presentation
EventType(workshop):
name: workshop
Event(first):
name: Drools and jBPM5
type: presentation
place: 64 rue Taitbout, PARIS
date: 06-17-2011 9:00:00
Event(second):
name: Search Engines
type: presentation
place: One Brattle Square, Cambridge
date: 06-23-2011 18:30:00
Event(Third):
name: Scale the Everest with Scala
type: presentation
place: One Brattle Square, Cambridge
date: 04-08-2011 20:00:00
Event(Fourth):
name: Play! framework, a java web framework for non-purists
type: workshop
place: 64 rue Taitbout, PARIS
date: 08-25-2012 20:00:00
Now we'll create a unit test to test our helper function dateDiff.
Rename test.BasicTest.java to DateDiffTest.java and enter the following code:
test:DateDiffTest.java
import java.util.Date;
import lib.utils.DateHelper;
import org.joda.time.DateTime;
import org.junit.Test;
import play.test.UnitTest;
public class DateDiffTest extends UnitTest {
@Test
public void dateDiffTest() {
assertEquals("1 second", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 30, 0, 0).toDate(),
new DateTime(2011, 1, 1, 10, 30, 1, 0).toDate()
));
assertEquals("10 seconds", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 30, 0, 0).toDate(),
new DateTime(2011, 1, 1, 10, 30, 10, 0).toDate()
));
assertEquals("5 minutes, 30 seconds", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 30, 0, 0).toDate(),
new DateTime(2011, 1, 1, 10, 35, 30, 0).toDate()
));
assertEquals("-5 minutes, -30 seconds", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 35, 30, 0).toDate(),
new DateTime(2011, 1, 1, 10, 30, 0, 0).toDate()
));
assertEquals("1 year, 2 months, 5 days, 3 hours, 15 minutes, 5 seconds", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 30, 0, 0).toDate(),
new DateTime(2012, 3, 6, 13, 45, 5, 0).toDate()
));
assertEquals("5 years, 5 months, 5 days, 5 hours, 5 minutes, 5 seconds", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 30, 10, 0).toDate(),
new DateTime(2016, 6, 6, 15, 35, 15, 0).toDate()
));
//singular form
assertEquals("1 year, 1 month, 1 day, 1 hour, 1 minute, 1 second", DateHelper.dateDiff(
new DateTime(2011, 1, 1, 10, 30, 10, 0).toDate(),
new DateTime(2012, 2, 2, 11, 31, 11, 0).toDate()
));
}
}
Now stop the application, and restart it in test mode. You can do it from the command line with the play test
command or from the Eclipse IDE selecting Run, Run configuration, test play-demo. Select DateDiffTest and run, it should pass the test.
A functional test lets you test your application accesing directly to your controllers. We'll use it to test our delete action. Rename AplicationTest.java to DeleteEventTest.java and enter the following code:
test:DeleteEventTest.java
import models.Event;
import org.junit.Before;
import org.junit.Test;
import play.db.jpa.JPA;
import play.mvc.Http.Response;
import play.test.Fixtures;
import play.test.FunctionalTest;
public class DeleteEventTest extends FunctionalTest {
@Before
public void deleteModels() {
Fixtures.deleteAllModels();
Fixtures.loadModels("data-test.yml");
}
@Test
public void deleteEventTest() {
final Long originalCount = Event.count();
final Event originalNextEvent = Event.find("order by date desc").first();
Response response = DELETE("/event/" + originalNextEvent.id);
assertStatus(302, response);
assertHeaderEquals("Location", "/", response);
assertEquals("There should be one less event", originalCount-1, Event.count());
assertNotSame("The next event must have changed",
originalNextEvent,
Event.find("order by date desc").first()
);
final Event deletedEventByCondition = Event.find("id = ?", originalNextEvent.id).first();
assertNull("The event should have been deleted", deletedEventByCondition);
}
}
In this test we get the id of the next event, then we issue and http request to delete it, and after that we check that the event has effectively been removed from the database. Notice how we use fixtures to work with known predefined set of events.
Acceptance tests are written using selenium tags, and running them with an automated browser. In this case we will first develop a test that deletes all events.
Here you have the selenium comand reference. You should have a look at all the commands available.
test: EventCrud.test.html
#{fixture delete:'all', load:'data-test.yml' /}
#{selenium}
// Open the home page, and check that no error occured
open('/')
assertNotTitle('Application error')
clickAndWait('xpath=//a[text()="new event"]')
clickAndWait('css=button[type="submit"]')
assertTextPresent('Oops! Errors detected')
assertTextPresent('You have to complete the event\'s name.')
assertTextPresent('You have to select the type of the event.')
assertTextPresent('You have to complete the event\'s place')
assertTextPresent('You have to complete the event\'s date.')
type('event.name', 'new event')
select( 'event.type.id', 'label=workshop')
type('event.place', 'event\'s place')
# test date format
type('event.date', '25-10-2011 18:30')
clickAndWait('css=button[type="submit"]')
assertTextPresent('Oops! Errors detected')
assertTextNotPresent('You have to complete the event\'s name.')
assertTextNotPresent('You have to select the type of the event.')
assertTextNotPresent('You have to complete the event\'s place')
assertTextNotPresent('You have to complete the event\'s date.')
assertTextPresent('Incorrect value')
# test date format
type('event.date', '10-25-2011 18:30')
clickAndWait('css=button[type="submit"]')
assertTextPresent('event successfully saved!')
assertTextPresent('new event')
# edit the newly created event
clickAndWait('xpath=//a[text()="new event"]')
type('event.name', 'new event modified')
clickAndWait('css=button[type="submit"]')
assertTextPresent('event successfully saved!')
assertTextPresent('new event modified')
# delete the newly create event
click('xpath=//a[text()="new event modified"]/../..//a/img/..')
waitForTextNotPresent('new event modified')
assertTextNotPresent('new event modified')
#{/selenium}
The next test will test the whole crud operation, that is it will create a new event, check that the validations are working, save the event, edit it, and finally delete it.
test: EventDelete.test.html
#{fixture delete:'all', load:'data-test.yml' /}
#{selenium}
// Open the home page, and check that no error occured
open('/')
assertNotTitle('Application error')
// Delete Play! framework event
assertTextPresent('Play! framework, a java web framework for non-purists')
click('xpath=//a[text()="Play! framework, a java web framework for non-purists"]/../..//a/img/..')
waitForTextNotPresent('Play! framework, un framework hecho en java.. para herejes')
// Search Engines
assertTextPresent('Search Engines')
click('xpath=//a[text()="Search Engines"]/../..//a/img/..')
waitForTextNotPresent('Search Engines')
// Delete Drools and jBPM5
assertTextPresent('Drools and jBPM5')
click('xpath=//a[text()="Drools and jBPM5"]/../..//a/img/..')
waitForTextNotPresent('Drools and jBPM5')
// Delete Scale the Everest with Scala
assertTextPresent('Scale the Everest with Scala')
click('xpath=//a[text()="Scale the Everest with Scala"]/../..//a/img/..')
waitForTextNotPresent('Scale the Everest with Scala')
open('/')
assertNotTitle('Application error')
assertTextPresent('No events in sight...')
clickAndWait('xpath=//a[text()="load from yaml file"]')
assertTextNotPresent('No events in sight...')
assertTextPresent('Play! framework, a java web framework for non-purists')
assertTextPresent('Search Engines')
assertTextPresent('Drools and jBPM5')
assertTextPresent('Scale the Everest with Scala')
#{/selenium}
If you are planning to integrate your tests with a continuous integration server, you can run all your tests with the command line play autotest
. The results of the test will be at the test-results folder.
More over, Guillaume Bort, the creator of play framework, developed a lightweight integration server with play. Here's the announcement on google group list, and it's being used with the play! framework project itself, you can check it at http://integration.playframework.org/.
How the play framework test runner works.
Play's documentation on testing
Now we are going to Step 13 - deploy to heroku.