Testing with robots

Now that we've covered a method of testing the fine detail of our code, let's look at a completely different way of running functional checks across the entire application. For this, we'll need a new tool: CasperJS. It allows you to drive a "headless browser"—one without any user interface—navigate around an application, and run evaluations on what we find. The first step is installation, which varies depending on the platform. Instructions for the same can be found at http://docs.casperjs.org/en/latest/installation.html.

When complete, we'll have a CasperJS command available to run.

With Jasmine, we were using the behavior-driven method of testing with expectations to verify the code. In CasperJS, we go back to using the assertion style of testing. Take a look at a minimal test script from the CasperJS documentation:

casper.test.begin("Hello, Test!", 1, function(test) {
    test.assert(true);
    test.done();
});

Pretty straightforward. The real magic comes when we combine this with CasperJS's ability to control the headless browser and interface with the web page that forms our application. Check this out:

casper.test.begin('Google search retrieves 10 or more results', 4, function suite(test) {
    casper.start('http://www.google.com/', function() {
        test.assertTitle('Google', 'google homepage title is the one expected'),
        test.assertExists('form[action="/search"]', 'main form is found'),
        casper.fill('form[action="/search"]', {
            q: 'casperjs'
        }, true);
    });

    casper.then(function() {
        test.assertUrlMatch(/q=casperjs/, 'search term has been submitted'),
        test.assertEval(function() {
            return __utils__.findAll('h3.r').length >= 10;
        }, 'google search for 'casperjs' retrieves 10 or more results'),
    });

    casper.run(function() {
        test.done();
    });
});

This looks a bit more exciting! Let's break down what's happening here:

  1. Create a new CasperJS test suite.
  2. Navigate to the Google home page.
  3. Assert that the page title is as expected and that we can find the search box.
  4. Fill in the search box and submit the form.
  5. Then, when the page has loaded, assert that the URL is as expected and contains our search query as a parameter.
  6. Assert that there are at least ten search results on the page.

Fantastic! This example shows how easy it's to use CasperJS to control a web page and how its testing features allow us to evaluate the content of the page and the behavior of an app.

The next step is to see how to use these features to test our own application, so let's hook CasperJS into our Alcohology project and get testing.

Up and running with CasperJS

Let's create a new subdirectory within our project at tests/casper and then create a new file in there called Sanity.js. We're going to write a couple of simple checks to make sure the application is loading correctly. Here's the starting point for the code:

casper.test.begin('Alcohology Sanity Checks', 0, function suite(test) {
    casper.start('http://localhost:1841/', function() {
    });
    casper.run(function() {
        test.done();
    });
});

We kick off by calling the casper.test.begin method, which starts off a new test suite and takes three arguments: a description for the suite, the number of tests we expect should be run, and a callback function that is called when the suite is created. The callback gets passed on a test object on which we can call various assert methods.

We then call CasperJS's start method with the URL to our application. In order to trigger the test run, we call CasperJS's run method and when everything's complete, we call the done method on the test object.

The first test we're going to write will check whether the category menu on the left-hand side of the application is populated as expected. To do this, we'll look for the first menu item and check whether it contains the text we expect, but it complicates matters slightly when we're loading this content in with Ajax. We need to be able to wait for the page to load, select the relevant element, and check whether it contains the content we'd expect.

In order to select elements, we're going to use the CSS selector, and so on, but we need a mechanism to find the correct selector to use. Fortunately, Chrome Developer Tools will come to our rescue once more; if we right click on the Pilsner text in the top category menu item in Alcohology, then pick Inspect Element, the elements panel will display with the menu item's element selected.

Next, right-click on the element and click on the Copy CSS Path option:

Up and running with CasperJS

The CSS select of this div tag will be copied to your clipboard and should look like this:

#gridview-1014-record-6 > tbody > tr > td > div

We can now use this with CasperJS:

casper.test.begin('Application sanity checks', 0, function suite(test) {
    casper.start('http://localhost:1841/', function() {
        var selector = '#gridview-1014-record-6 > tbody > tr > td > div';

        casper.waitForSelector(selector, function() {
            test.assertSelectorHasText(selector, 'Pilsner'),
        });
    });

    casper.run(function() {
        test.done();
    });
});

After telling CasperJS to start and load the application's web page, we will use the waitForSelector method to wait until the specified selector appears on the page; by default, it'll wait for 5 seconds before throwing up a failure message. When the selector appears, the callback function is triggered and we use the assertSelectorHasText method to check whether the div tag has the correct text:

Up and running with CasperJS

Running our first CasperJS test

It's simple, but effective. If we'd broken something due to an amend in the code for the categories store, incorrectly bound the data to the view model, or some other minor change that cascaded to affect this key feature, then this test would immediately flag it up.

Note

CasperJS relies on another library called PhantomJS to drive the headless browser. There's an issue between the two in the current version that causes the Unsafe JavaScript attempt… message that you can see in the preceding screenshot; it's fine to ignore it.

We can do a lot more. Although the correct text is showing, the menu isn't just for display purposes, and when a user clicks on a menu item, it will load the products with this category. Can we create a CasperJS test for this?

Of course we can! Take a look at the CasperJS API documentation at http://docs.casperjs.org/en/latest/modules/casper.html#casper-prototype.

Alongside the start method, we have ones such as fill that allows you to complete form fields: scrollTo, which lets us move to a particular location on the page, and for our current purposes: click, it provides a means to click on an element specified by a selector. Let's build another test using click to run a more advanced set of steps, something like this:

  • Load the application
  • Click on the IPA category
  • Select the Lagunitas IPA product
  • Check whether the product window appear
  • Check whether the correct product name and price appear

This is a much more comprehensive test that goes some way to demonstrating the power of CasperJS to replicate user actions and validate the behavior of our application. Here's the code:

casper.test.begin('Product walk through', 2, function suite(test) {
    casper.start('http://localhost:1841/', function() {
        var categorySelector = '.categories-body table:nth-child(2) td',
            productSelector = '.product-list .product:nth-child(2)',
            windowSelector = '.product-detail',
            headerSelector = '.product-detail h1',
            priceSelector = '.product-detail p.price';

        // Wait for the categories to load.
        casper.waitForSelector(categorySelector, function() {
            // Click the specified category.
            casper.click(categorySelector);
        });

        // Wait for the category products to load.
        casper.waitForSelector(productSelector, function() {
            // Click the specified product.
            casper.click(productSelector);
        });

        // Wait for the product window to appear.
        casper.waitForSelector(windowSelector, function() {
            // Assert text for heading and price.
            test.assertSelectorHasText(headerSelector, 'Lagunitas IPA'),
            test.assertSelectorHasText(priceSelector, '£12.99'),

            // Capture a screenshot.
            casper.capture('products-page.png'),
        });
    });

    casper.run(function() {
        test.done();
    });
});

The comments in the code should make it pretty self-explanatory. After setting out all of the selectors we need, we wait for the categories and click on the one that we need. Next, we wait for the product we want and click on it before waiting for the product window to appear and asserting on its contents. For the final trick, we instruct CasperJS to take a screenshot, a feature that can be useful for debugging or further evaluation.

Running this code gives us the following screenshot:

Up and running with CasperJS

Success! We've simulated and validated a small user path through the Alcohology application, checking whether several moving parts of our project work in tandem as expected.

The eagle-eyed reader will notice that the selectors in this example look a little more friendly than the ones in the first example that we grabbed using Google Chrome. There's a very good reason for this and it relates to a set of ideas surrounding making your application easier to test.

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

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