I'll also briefly touch on automated cross-browser testing, and how to get your tests running in Jenkins.
It's really easy to look at this and think "it's going to be too hard to test". None of the code is available in the global scope, and it's all wrapped up in this jQuery
ajax call anyway.
Luckily, some pretty smart people have figured this out already. There are a bunch of amazing tools out there which not only make testing this code possible, they make testing this code relatively easy.
So let's write a test for it.
I'm going to be using Mocha, Chai, and Sinon to write these tests. There are plenty of other popular choices available, for example Jasmine, QUnit, Buster.JS... As far as I'm concerned it doesn't matter what you use, as long as the end result is expressive, self-documenting tests.
It has a bunch of different reporters, including a familiar-looking command-line reporter and an HTML reporter.
Chai is an assertion library. It has a few different syntaxes, which you can use to write some really expressive, self-documenting tests.
Stubs and mocks are absolutely essential when you want to test code like the snippet I showed before. They're going to allow you to test the way your code uses third-party libraries.
In the first example, Sinon is replacing jQuery's
ajax method with a stub. The stub keeps track of all the times it's been called and what arguments it was called with. This means you can write assertions like this one here, which ensures the
ajax method is called with an object containing "url: /foo/bar".
Sinon also has some great utilities including a fake XHR for testing your AJAX calls, and a fake timer for testing code that relies on time-related functions like
The second example here shows the fake timer being used. There's a div which is 100 pixels wide. jQuery's
So, back to our code. How do we start testing something like this?
I mentioned before that none of this is in the global scope, which is a problem. So we actually need to make a small change to this code to make it testable.
It's not the cleanest solution, but it's going to allow us to test this code without doing some heavy refactoring.
Here's a simple test for this code. I'll take you through it.
We create a mock for jQuery using Sinon. At this point, the jQuery object isn't modified.
Now we tell the mock that it should expect the
ajax method to be called exactly once. This is where Sinon actually modifies the jQuery object and swaps out the
ajax method for a stub method.
...And this bit is why Sinon is awesome. After the
ajax method is called, Sinon is going to look at the arguments it was passed, find the
success property in those arguments, and then call it with the data we specify.
This basically allows us to keep the behaviour of jQuery's
ajax method, without having to write our own complicated mock for it.
So if you're following, our code's
success method is going to be called with this object containing "Message: Hello".
Now we actually run our code.
And finally, we get Sinon to verify all of the mock's expectations. If the mock is called in a way that we don't expect...
... The tests will fail. (In this example the mock is expecting to have its
get method called, but it never is).
[This slide is for context]
We could take our mock even further and check that our code does actually make a POST request.
And we can go to any level of detail here, depending how strict we want the test to be.
To be honest, I'd be cautious about going into this much detail. Mocks are really powerful because they allow you to test stuff like this, but putting too much detail into your mocks can make your tests really brittle.
Right, what if we want to take this a step further and test the
success callback? We could write a test to make sure that the response is put into the DOM and shown to the user.
We're going to use Sinon's
stub functionality to test this.
What's happening here, is we're creating a
$node stub object. This stub contains the
show functions that we're expecting to be called.
Then we stub the global "jQuery dollar" and force it to return our
So, just to clarify, [<]
$('.foo') is going to return [>] this
$node object. And because we've stubbed this
$node object, we can track how it's been used...
So we can assert that
$node.show has been called exactly once. And that
$node.text has been called exactly once, with "Hello" as the first parameter.
So far I've just run the tests with Mocha on the command line, which means the tests have been running on Node.js. The next step is to get them running in an actual browser.
Getting Grunt and Karma set up requires a little bit of boilerplate, which I'll go through now. If you want some examples to help you get set up, there are a few projects in our GitHub that you can copy from as well.
... I recommend checking out Yeoman, which can generate stuff like this for you.
Right, the first thing is to define some dependencies in
package.json and run
Then you define some tasks in a Gruntfile, which is the equivalent of Phing's build.xml.
Then you can run Grunt.
So I mentioned that Karma can run on any browser, on any device. The Karma team have written launchers for a bunch of browsers.
All you need to do is specify which browsers to run in the Karma configuration. The only catch is that these browsers need to be installed on the system that is running the tests, which means that you can't run cross-browser tests on the buildslaves. However...
...There are also Karma launchers for Sauce Labs and BrowserStack, which are essentially cross-browser-testing-as-a-service tools.
But if all of that still doesn't cover your needs, you can get Karma to launch a custom browser or script, or you can write your own launcher plugin.
Getting this all running in Jenkins is super easy. You just point Jenkins at your repository, and get it to run
npm install and
So at this point, you've got a job in Jenkins in which you can install any NPM module, and run it with Grunt.
This is pretty powerful, and you can do a lot with it.
You can generate JSHint reports.
You can generate JSUnit reports from Karma.
You can even write your tests in CoffeeScript if you're that way inclined.
And when you've got your tests running, you can display this sweet-as build badge on your repository.