Back to Resources

Blog

Posted January 28, 2014

Guest Post: Front End Testing with Webdriver

quote

This week's guest blog post comes from Peter Braden. Peter is a founder at Frozen Ridge - a full-stack consultancy that specializes in solving hard problems for businesses and provides services around node.js, Continuous Integration and Databases. Frozen Ridge are Sauce Labs partners and are available for training and high level consultancy around Continuous Integration and Front-End testing.

--------------------------------------------------------------

At a recent training, I gave a talk on integration testing Front-End code with Webdriver. The tools to test Front-End code have improved a lot recently, so I thought I'd write a blog post about the current state of the art. Luckily browsers have come a long way in the last few years, and many of us are no longer forced to support decades-old browsers like the dreaded IE6. But a lot of the time when working on complex UI's or web applications, we're working with DOM heavy code that isn't easily unit testable. And without some forethought, it's easy to get stuck in the nightmare of click-testing your code. Luckily it doesn't have to be this way. One of the problems with testing front-end code is that a lot of the stuff we want to test doesn't readily fit into a discrete 'unit' of code, so unit testing is often laborious. When each test requires an elaborate sequence of steps to be set up, it is often easier to justify just cliking your way through testing, at least while you develop the code. Test-First development is difficult on the front-end and when you're trying to unit-test, it can become untenable.

As a result, I've stopped writing front-end unit tests. I don't think unit testing is a model that fits for most front-end scenarios. Instead I write integration tests for specific flows. I think this model fits a lot more nicely.

Integration Testing with Chromedriver

The first thing we're going to need to write front-end tests is a browser that we can easily script. For CI I use browsers in the cloud with Sauce Labs, but when I'm just writing code, I want the browser to run locally so I can easily debug any errors that occur.

The easiest way I've found is to use Chromedriver locally.

Run Chromedriver Daemon:

2. Run the Chromedriver Daemon:

chromedriver --url-base=wd/hub --verbose=1

(There's a bug with chromedriver on OSX Mavericks that can be fixed by running in verbose mode)

Spawn a browser

Now that we have the Chromedriver daemon running, we need to spawn a browser programatically.

We're going to be writing our tests in node.js using the wd module:

npm install wd

And in test.js:

var wd = require('wd') var browser = wd.promiseChainRemote({hostname:"localhost", port: 9515}) browser.init({browserName: "chrome", "version" : ""}, function(){   browser.get("https://www.google.com/")   // Your tests here...     .fin(function(){       browser.quit()     })     .done() })

As you can see if you run the above code, a chrome browser will be run, will navigate to google, and will then kill itself.We're nearly there.

Test your flows

When you are writing a web application, you typically think in terms of flows the sequence of actions and events that a user will perform in use of a feature.For example, you may have a 'forgot password' flow:- User clicks 'forgot password' link - Link redirects to '/password/forgot' - Page contains form with email address field - User types email into field - User submits form - Email should be sent containing reset codeEssentially, we want to test permutations of this flow by simulating a user performing each action. It's not really a 'unit' to be tested, but nonetheless knowing that this flow works is important.Webdriver makes it fairly trivial to simulate user actions:

browser.get('/login') .elementById('forgot-password') .click()

.waitForVisibleById('forgot-password-form') .elementById('email-address') .type('hi@test-email.com')

.elementById('submit-forgot') .click(function(err, res){ // The form should have been submitted - we can then check our database to see if // a reset code has been generated: assert.isNull(err) mydb.reset_codes.find({email: "hi@test-email.com"}, function(err, res){ assert.isNull(err) assert.ok(res) }) })

And as simply as that we've simulated a user going through the flow. Because setting up test users with fixtures etc. is a little tedious, I prefer to make my flow tests fairly long and involved, and check that things haven't failed periodically throughout the execution. Because the flow test will fail as soon as _anything_ breaks, you won't necessarily get any subsequent testing, but when I'm testing front-end code, I'm far more interested in having comprehensive smoke tests, because if anything breaks, I want to test the entire flow anyway. Because webdriver can also use cloud-based browsers, these tests can be run in your CI system in multiple browsers very easily, meaning that you can develop with a test-first mentality, and have that easily transfer into a CI environment.

Published:
Jan 28, 2014
Share this post
Copy Share Link
© 2023 Sauce Labs Inc., all rights reserved. SAUCE and SAUCE LABS are registered trademarks owned by Sauce Labs Inc. in the United States, EU, and may be registered in other jurisdictions.