Testing workflows

In this last section, we'll cover another level of tests that is able to test exposed features involving workflows crossing the atomic services provided by an application. This level of testing is also able to test interfaces opened to the wild, such as HTTP REST interfaces. These tests are probably the most important ones because they're asserting that our application is presenting features to the end user and these features are working well. That is, they are asserting that we've created added value to our application for the end user.

These kinds of tests are also the most difficult ones because they include third-party products or infrastructure components such as a browser and an HTTP server. However, we'll see that Play! Framework 2 is aware of these requirements, and it prepares everything for us in order to let us focus on the test logic only.

As in the preceding sections, several dedicated helpers are available for our tests. The first one is an overloaded version of the running helper. This version has an extra parameter, a server. Since it can be quite cumbersome to create or integrate a server in our test environment, Play! Framework 2 has defined a wrapper around a Netty server that is accessible through the TestServer class.

A TestServer instance is created using three parameters:

  • The port where the server must listen
  • An Application to be run within the server
  • An optional SSL port

By having such a server running, it'll be possible to test the features, such as the Twitter proxy in our application, that we're exposing to the outside world. Let's see an example:

Testing workflows

What has been done here is that after the classical test structure, we used the running helper with a TestServer that must listen for HTTP requests on port 3333 and that starts a simple FakeApplication like before.

Then we directly took the opportunity to use this server by defining a call to our own application, targeting the Twitter controller, especially the mentioning action.

Such a call is exactly the same as it would be for any other web service, as in this case, our application is also a web service. So, we're using the WS API that will hit the test server on port 3333 by using the routed path for this action.

Recall that this WS#url method will return a promise of a result, that is to say that the result won't be available until we explicitly wait for it. Waiting for Promise to return can be done using another helper called await.

This await helper takes a Promise instance and blocks the current thread until the underlying promised result has arrived; then it returns this result back. That's why the value of the variable response is already the complete response of the web service.

After doing a sanity check on the response status and confirming that it is OK, we directly moved to a more specific one by asserting that all tweets must contain the expected username.

Since the mentioning action is returning a result with a JSON-encoded body, we can use the double backslashes (\) operator on it to retrieve all tweet messages. This will return a sequence of the value of all the properties named "tweet" in the JSON tree.

For this, we have to first parse the body of the response as JSON using the json method, after which we will be able to extract all the tweets out of it. The rest of the test is a specs2-specific check for sequenced content.

All we said is that the text of all the tweets in the sequence must have a type String (otherwise the test fails), and each of them must also contain the searched username (noootsab).

Launching the test in the console will give the report shown in the following screenshot:

Testing workflows

Success!

This example is representative enough of what can be a service's feature test; however, an application is not only composed of services to be used by other programs. A web application targets real users most of the time, who are using browsers and clicking and entering text and so on using a keyboard or mouse.

Note

We won't talk about mobile-native clients in this book, but since Play! Framework 2 is not embedding anything to build such applications, it's acceptable. However, these native applications are to be tested alone, in which the used services are assumed to be correct.

For such high-level tests, Play! 2 uses Selenium. Even if we can use the classical version of the Selenium API to test our stuff, Play! 2 will integrate a rather better API on top of it called FluentLenium .

Selenium (and its wrapper) is able to either emulate a browser in-memory or use specific drivers to launch tests. To use these drivers, we have to first set up our machine with the targeted browsers (Safari, Firefox, Chrome, and others) and install the associated Selenium web drivers .

Note

Since it's not the purpose of this book to provide in-depth details on how to efficiently use Selenium, we'll use the in-memory version of the browser that doesn't require any other setup.

What we're going to do now is to see how easy it is to test a web application end to end involving as many layers as the application is using.

To illustrate this, we'll test an unregistered user logging in to our wonderful web application chatrum. Since he/she really wants to use it, he/she will have to go on the register page wherein he/she will have to enter all the information required by the HTML form (and the related action on the server side).

Having submitted the registration form, he/she will be able to connect to the application using e-mail and then use the dashboard.

This workflow will require the test to simulate a lot of things on the client side, click on buttons, type text in some input fields, select a box or drop-down lists, and so on.

Under the sea, we'll need the full server to check the validity of the inputs, the existence of the user, and to store information in the session.

This kind of setup can become a nightmare really quickly, but not with Play! Framework 2. Here is how we're supposed to do what is presented in the following screenshot:

Testing workflows

So easy! There are only two things we have to do compared to the previous test, as follows:

  • Pass a second argument to the running helper, which is the web driver we want Selenium to use—constants such as HTMLUNIT and FIREFOX are available in the helper class (Helpers.scala) and they define the web driver to be used for related browsers.
  • The content of the running helper, rather than being a simple block, is now a function taking one argument called browser, which is an instance of the web driver. Note that the creation of this instance is out of our hands because it will be handled by the framework.

Another point is to use this driver to simulate a user navigating the application, generating data and requests. We can do this using FluentLenium, as follows:

Testing workflows

Wow... we've done a lot. It'd be worth reviewing it a bit, so let's do it step by step.

Firstly, we'll store the base URL of our application and then we ask the browser to navigate to the login page.

Testing workflows

Since we're running a server on port 3333 (using the TestServer class), our web application's base URL is obviously the value of baseUrl.

Then we ask the browser to navigate to the login page by using its endpoint (/login) as configured in the route file. Since the user has not been registered yet, he/she has to click on the link to be redirected to the register page.

Testing workflows

Some interesting things here. First, the browser object has a method $, like jQuery, that enables us to search the DOM for elements. This find method (its other name) takes a CSS selector and supports most CSS3 features (pseudo classes, attributes, and so on). In our case, we locate the link (<a> tag) and ask Selenium to simulate a click on it by simply calling the click method on the element.

Given that this link redirects to the register page, we can check right after that the new browser's URL is now pointing to the route of the register action. But also, we can check that artifacts are present on the page, like the example shown previously that checks that the first label HTML element under the gender_radio input HTML element contains the Female string. The register user page shows a form containing a lot of information, such as the username and e-mail address. What we must do now is fill them all. For this, we'll start by filling the textual inputs, then we'll deal with the radio buttons and select boxes.

Testing workflows

Filling an HTML form can be done using the browser's fill method that takes two parameters to retrieve all the form elements that are to be filled in. The first parameter is the CSS selector; the second can be used to add constraints on the found elements. In this example, we fetched all the input elements and then removed the radio ones (using the type attribute).

Note

You may have noticed the backticks around the with method. This is because with is a reserved keyword in Scala, and Scala enables us to use tricky names or reserved words if they are surrounded by backticks.

Having collected all the needed elements in the form, we can now ask them to receive values. This can be achieved using another with method, by passing it the values in the same order as the order of the elements in the form.

That was easy for the text-based input fields, but our form has two other fields, namely a radio button (gender) and a select one (country). Both of them don't take String as a value but react to clicks, so we asked the browser to search for them and click on the relevant parts.

Now that the form has been filled completely, it's time to submit it. This will be done with the submit method on the browser using the form element to be submitted. Submitting a form requires a server-side action that might take some time to return. This is why we can ask Selenium to wait a certain amount of time until the server replies. In our case, we asked the test to wait until an input element with a named email appears in the DOM (Document Object Model) simply because the login page has it, and register should redirect to the login page.

Testing workflows

We were asked to wait for a second until the input named email was present. When this field was present, we did the same thing again with the login form; we set the e-mail value and then submitted it. So now we're logging the user in.

To end the test, we should now check that the result of the login action is the dashboard page itself.

Testing workflows

Before any checks, we asked the browser to wait for an element with an ID that is equal to dashboard to be present. Then we checked that the URL is the expected one (the route of the Dashboard#index action). Finally, we verified that the h1 tag has a text containing the e-mail used to register the user and log him/her in.

Starting from here, we can now envision all the tests we could perform for for workflows that involve a chat, the creation of topics, and so on.

At this stage, we've reached the higher level of functional testing without having to set up anything more than what was set up for the application itself.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset