A software development stack that does not include testing, in the age of test-driven development (TDD), is like shooting itself in the foot. A web framework that is involved transversally with the runtime environment should especially enable the developer to assert all phases of his/her work – from core logic to an HTML presentation through business logic.
Thankfully, Play! Framework 2 is a very good web framework. It provides plenty of helpers to test all those layers. Those helpers will be helpful not only in unit testing but also in applicative tests (business) or functional ones (UI, REST, and so on).
Even though Play! 2 can be integrated with either the Java or Scala testing frameworks, in this book we'll focus on Scala testing for both Java and Scala applications. That's because testing is a perfect way to start learning Scala, resulting in the fact that a test code need not be highly efficient by essence and shouldn't include any core logic at all. In short, its implementation is not critical and shouldn't be visible to final users.
A last note before going into much detail, for those who have used the first version of the Play! Framework; at the time writing, the way to execute tests has changed a lot. Indeed, in the first version, we were able to launch tests through a dedicated URL while running the application in DEV mode and we were presented with an HTML page where tests could be run by clicking an item. This feature hasn't been recovered yet in this second version. We'll see in the next sections how things are going now.
In this chapter we will:
A web application is built on several layers, each of them having their own responsibilities, such as storage, transport, or business. That's probably why it's so difficult to test an application like this as a whole.
Indeed, most of the time a unit test, or what could be considered as a unit piece of the software, will require boilerplates or mock-ups to run it.
The perfect example is fetching a user's information using the REST API our application is exposing. This will require us to have a database, an HTTP broker, and so on. But still it should be considered as a unit test. No business logic, no specific requirements, just a GET method using an ID.
That's why in a web application there exist tests that I'm calling atomic. These tests don't require a specific environment to be run and, of course, are the simplest tests – they can be seen as plain unit tests in a utility library, for instance.
A famous testing framework in Scala is specs2. specs2 has an amazing number of features that shall require a full book and, actually, the user guide is already one in itself (http://etorreborre.github.com/specs2/guide/org.specs2.UserGuide.html). However, we'll see some of them in action in the following sections.
The principle that resides within specs2 is the definition of specifications, which are kind-of readable sentences that describe the tests you're performing and allow you to both define unit as well as acceptance tests.
Roughly, a specification is structured as several layers. The first layer defines the goal of the specification. Then it will contain several fragments that include the test code and return a Result
class—a specs2 one—such as a standard status (ok
, failure
, and so on) or a matcher (such as something must be not null
).
specs2 also has two different notations for defining tests, which are the unit and the acceptance notations. We'll use the unit one for the rest of the book because it offers the more intuitive DSL.
So let's write an atomic test for our comparison code (back to Chapter 2, Scala – Taking the First Step) between Java and Scala. But let's test the Java implementation only in Scala!
What we'll test are the high-order functions that were created in order to draw some parallelism between Java and Scala. These are gathered in the comparison.Sequence.java
file.
The root folder, where the tests files are expected to be in Play! 2, is test
, right under the root of the application; that is, sibling to app
.
So in order to write our tests, we'll create a folder in tests/atomic
and a file named ComparisonTest.scala
.
Here is how simple tests would look and how we can run them:
In the previous screenshot, we can see several tests of the comparison.Sequence
functions we've implemented in Chapter 2, Scala – Taking the First Step. We have at least one test by function.
It should be worth reviewing it a bit now before seeing them run.
First of all, a specification has to extend the org.specs2.mutable.Specification
class, which expects in its body the definition of at least one specification. Such a definition must start with a string message declaring the topic of the specification; in this case, we test Sequence
. This message will be used to give an intuitive print of the tests in the console.
Having defined the topic, we have to declare what this topic should respect. That's the role of the following fragments that have been introduced using the should
method on the "topic". In most cases, a fragment is some information separated by the in
method, a human-readable description of the message (one line) and the testing block. Looking at the sample, we can see that those couples can be chained in order to create several fragments to be checked all in one row.
So far so good, but what are those blocks defining how we can create some assertions? For this, we need to review the testing blocks. Let's do it one by one, since they're using different matchers
. A matcher in specs2 can be seen as assertThat
in JUnit so that it can construct a complex check but also be composed. There are plenty of different matchers provided by the specs2 framework and others provided by the Play! Framework 2 as well (we'll see them in the following sections). There are five fragments being defined in the sample shown in the previous screenshot. As mentioned before, each of them return Results
(of which matcher is a subtype).
The key point is the must
method that can be used on any computation. This method's role is to take a predicate to assert the correctness of the computed value.
OK, this time we can see how to do some checks. The first check for the even
function, which returns all even numbers in the Sequence
list, is asserting that the resulting list will exactly match the expected one. For that we take the result of the computation and say that it must be identical to the provided expected List
of 2
and 4
. An equality comparison is done using the be_==
operator. You guessed it; there are other such comparators such as be_<=
and so on.
Moving to the second test (fragment), we checked that the result of squaring each element in the list is equal to the provided List
of squares. This is cool, but hardcoded. Even if the comparison code is using the same List
instance all the time, in the real world those functions must work on any List
instance. So we would like to assert that the function is respecting its contract
; for instance, the even
function execution must always return a List
that is composed of even numbers only. This is shown in the third test wherein we asked the result to have all elements respecting the provided pattern. In this case, the pattern is simply the item itself (which is inferred to be an Int
), but it must be a multiple of 2
.
The fourth test is a bit more advanced (OK, not that much) because its result involves a conjunction of two assertions, one of them being a simple Boolean check using the beTrue
operator. The second is the negation (using not
) of an unsafe result (that throws a NoSuchElementException
). For this last point, it'd be worth noting that None
that is extending Option<A>
, the result type of find
, is throwing an exception when trying to get the underlying value.
And finally, the last check is simply asserting a false result.
That was easy. Our tests have been written; let's see now how we can run them.
In this second version of the Play! Framework, the test environment configuration and their runs have been delegated to the build tool SBT. Hence, to run the tests we must enter the play console, and rather than launching the run
command, we can execute the test
command. This command has the responsibility to compile everything, including the sources in the test
folder, and then run all tests in there.
Here is the result for our tests.
Wow, what are those errors, crosses, and scary messages shown there? Actually, that's why tests are written, to discover mistakes. And this is the result we'll get when mistakes are found. The result of the tests shown here is telling us that four tests out of five were successful, so one has failed. Finding which one is pretty easy since it's the only one in the tests summary that has an orange cross whereas the others have beautiful green plus signs.
Before looking at the failure, we shall take a look at what's being printed by the framework on the console.
Indeed, since we respected the structure and the text content, for which we've been helped by the DSL (Domain Specific Language) itself (using methods named should
, in
, must
, and so on), we can now take the output and read it from the top; line by line it provides the following output:
even
squareSeq
These sentences simply describe what the test will do.
Back to the test that has failed; we notice that the framework is literally telling us that the result list (printed first) isn't equal to the expected list (printed later).
Then it prints the list again with their role in the test and where they differ.
So it seems our squareSeq
function is buggy (it's true); here it is:
See? Indeed, rather than computing the square of each element
, we computed the nth power of two of element
, which can be checked easily since the test's result has printed the actual List
containing values such as 1 (20)
, 2 (21)
, 4 (22)
, 8 (23)
, 16 (24)
, and 32 (25)
.
The fix is rather trivial; just swapping the arguments will do the trick. After that change, running the tests again will result in the following screenshot:
No more red!
That was a lot of fun, but atomic tests are not the only tests we need while creating a web application. Most of the time actually, they're in the minority. Because of the architecture of a web application, we mostly need tests that involve the server itself, or at least a part of it.
In the Play! Framework 2, the main component at runtime is the Application
singleton itself, which is a piece of software that can do everything unless it is working as an HTTP server.
Nevertheless, this is very common because we'll be able to test artifacts such as controllers.