§Testing your application
Writing tests for your application can be an involved process. Play supports JUnit and provides helpers and application stubs to make testing your application as easy as possible.
§Overview
The location for tests is in the “test” folder. There are two sample test files created in the test folder which can be used as templates.
You can run tests from the sbt console.
- To run all tests, run
test
. - To run only one test class, run
testOnly
followed by the name of the class i.e.testOnly my.namespace.MyTest
. - To run only the tests that have failed, run
testQuick
. - To run tests continually, run a command with a tilde in front, i.e.
~testQuick
. - To access test helpers such as
FakeApplication
in console, runtest:console
.
Testing in Play is based on sbt, and a full description is available in the testing documentation.
§Using JUnit
The default way to test a Play application is with JUnit.
import static org.junit.Assert.*;
import org.junit.Test;
public class SimpleTest {
@Test
public void testSum() {
int a = 1 + 1;
assertEquals(2, a);
}
@Test
public void testString() {
String str = "Hello world";
assertFalse(str.isEmpty());
}
}
Note: A new process is forked each time
test
ortest-only
is run. The new process uses default JVM settings. Custom settings can be added tobuild.sbt
. For example:javaOptions in Test ++= Seq( "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9998", "-Xms512M", "-Xmx1536M", "-Xss1M", "-XX:MaxPermSize=384M" )
§Assertions & Matchers
Some developers prefer to write their assertions in a more fluent style than JUnit asserts. Popular libraries for other assertion styles are included for convenience.
Hamcrest matchers:
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class HamcrestTest {
@Test
public void testString() {
String str = "good";
assertThat(str, allOf(equalTo("good"), startsWith("goo")));
}
}
§Mocks
Mocks are used to isolate unit tests against external dependencies. For example, if your class under test depends on an external data access class, you can mock this to provide controlled data and eliminate the need for an external data resource.
The Mockito library is a popular mocking framework for Java. To use it in your tests add a dependency on the mockito-core
artifact to your build.sbt
file. For example:
libraryDependencies += "org.mockito" % "mockito-core" % "2.10.0" % "test"
You can find the current version number of mockito-core
here.
Using Mockito, you can mock classes or interfaces like so:
import static org.mockito.Mockito.*;
// Create and train mock
List<String> mockedList = mock(List.class);
when(mockedList.get(0)).thenReturn("first");
// check value
assertEquals("first", mockedList.get(0));
// verify interaction
verify(mockedList).get(0);
§Unit testing models
Let’s assume we have the following data model:
public class User {
private Integer id;
private String name;
public User(final Integer id, final String name) {
this.id = id;
this.name = name;
}
}
public class Role {
private String name;
public Role(final String name) {
this.name = name;
}
}
Some data access libraries such as Ebean allow you to put data access logic directly in your model classes using static methods. This can make mocking a data dependency tricky.
A common approach for testability is to keep the models isolated from the database and as much logic as possible, and abstract database access behind a repository interface.
public interface UserRepository {
public Set<Role> findUserRoles(User user);
}
public class UserRepositoryEbean implements UserRepository {
@Override
public Set<Role> findUserRoles(User user) {
// Get roles from DB
...
}
}
Then use a service that contains your repository to interact with your models:
public class UserService {
private final UserRepository userRepository;
public UserService(final UserRepository userRepository) {
this.userRepository = userRepository;
}
public boolean isAdmin(final User user) {
final Set<Role> roles = userRepository.findUserRoles(user);
for (Role role : roles) {
if (role.name.equals("ADMIN")) return true;
}
return false;
}
}
In this way, the UserService.isAdmin
method can be tested by mocking the UserRepository
dependency:
@Test
public void testIsAdmin() {
// Create and train mock repository
UserRepository repositoryMock = mock(UserRepository.class);
Set<Role> roles = new HashSet<Role>();
roles.add(new Role("ADMIN"));
when(repositoryMock.findUserRoles(any(User.class))).thenReturn(roles);
// Test Service
UserService userService = new UserService(repositoryMock);
User user = new User(1, "Johnny Utah");
assertTrue(userService.isAdmin(user));
verify(repositoryMock).findUserRoles(user);
}
Note: Applications using Ebean ORM may be written to rely on Play’s automatic getter/setter generation. If this is your case, check how Play enhancer sbt plugin works.
§Unit testing controllers
You can test your controllers using Play’s test helpers to extract useful properties.
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static play.mvc.Http.Status.OK;
import static play.test.Helpers.*;
import javaguide.tests.controllers.HomeController;
import org.junit.Test;
import play.mvc.Result;
import play.twirl.api.Content;
public class ControllerTest {
@Test
public void testIndex() {
Result result = new HomeController().index();
assertEquals(OK, result.status());
assertEquals("text/html", result.contentType().get());
assertEquals("utf-8", result.charset().get());
assertTrue(contentAsString(result).contains("Welcome"));
}
}
§Unit testing view templates
As a template is a just a method, you can execute it from a test and check the result:
@Test
public void renderTemplate() {
Content html = views.html.index.render("Welcome to Play!");
assertEquals("text/html", html.contentType());
assertTrue(contentAsString(html).contains("Welcome to Play!"));
}
§Unit testing with Messages
If you need a play.i18n.MessagesApi
instance for unit testing, you can use play.test.Helpers.stubMessagesApi()
to provide one:
@Test
public void renderMessages() {
Langs langs = new Langs(new play.api.i18n.DefaultLangs());
Map<String, String> messagesMap = Collections.singletonMap("foo", "bar");
Map<String, Map<String, String>> langMap =
Collections.singletonMap(Lang.defaultLang().code(), messagesMap);
MessagesApi messagesApi = play.test.Helpers.stubMessagesApi(langMap, langs);
Messages messages = messagesApi.preferred(langs.availables());
assertEquals(messages.at("foo"), "bar");
}
Next: Writing functional tests