Quantcast
Viewing all articles
Browse latest Browse all 7

Getting started with JS and TDD

Introduction

Are you working on unmaintainable JavaScript code? Are you afraid to make changes to your 5000+ lines long JS file? If so, you’ve probably not written tests or done TDD. Fear not! Read on, and we will show you how to get started with writing tests and setting up the needed tools. We will show by example how to create a small, yet complex, JS web application using TDD.

JavaScript is becoming increasingly popular for developing full-fledged applications – both server and client side. Looking at the current trend of how JS is used, we can see that it’s being used for business critical applications and large-scale applications. With all this popularity and extensive use, we are under the impression that the testing and quality focus is lagging behind.

After reading this blog post, you will know how to get started with JS TDD using tools and frameworks we consider easy to use and relatively mature. If you already know why you should test your JS code, feel free to skip the background.

If you want to take a closer look at the example application, feel free to check out the entire project from GitHub @ http://github.com/bekk/jstdd.

Background

Image may be NSFW.
Clik here to view.
The JavaScript logo
JavaScript has received a popularity boost from browser vendor’s quest to write the fastest JS engine, as well as the popular programming language node.js. In addition, jQuery has simplified the way we interact with the DOM and smooths over annoying browser differences – making JS more accessible for everyday developers. Focus have been primarily on producing functionality, and not on testing and good design/clean code. We believe that one reason is the lack of standardized or commonly available testing tools and frameworks. Another reason might be that traditionally JS have been written by UI and UX developers, who’s focus have been primarily on functionality – not on Clean Code and testability.

JS and all other dynamic programming languages is in dire need of tests since there is no compile-time checking. Tests can be used to ensure that the application behaves as expected, and gives developers confidence to make changes to existing code. Despite the obvious need for testing, many developers skimp on writing tests when developing large JS applications. All the advantages of TDD that applies for other programming languages, also applies for JS. Who does not want testable, maintainable, readable, well-designed, modular and working code? It’s well established that TDD has positive effects on code quality (read the chapter on TDD by Robert C. Martin in The Clean Coder).

Other popular programming languages like Java and C# have mature and well-documented testing-tools. At the time of writing, JS has several testing frameworks available, but only a handful mature. Looking at the options, no one really stands out as a clear choice. To make things worse, getting started with JavaScript TDD can be challenging. There are so many ways of going about setting up a complete test-environment for JS. We have looked into this and had some experience with what works and what doesn’t.

The runner

JsTestDriver is (as it’s name implies) a tool that let you run your JS tests. You can run your tests directly from your favorite IDE (at least in theory) to get into your TDD rhythm. It runs from the command line and even on your continuous integration server. In our examples we’ll be running it from the command line.

A quote from their home site says it all: “The goal of JS Test Driver is to make JavaScript unit test development as seamless as and easy as Java unit tests.”

JsTestDriver supports testing in different browsers through capturing browsers as a slave machines. It includes it’s own limited assertion framework, but also supports add-ons, which opens for a wide range of third party assertion frameworks. We will be using Jasmine BDD through an adapter.

As with everything else, there is room for improvement. JsTestDriver’s configuration can sometimes be too simple, and advanced users may feel limited. In our simple example it works great, but expect some issues when scaling up. As a note, we did not get the IDE integration in IntelliJ IDEA to work properly.

Recently Buster.js was released as beta, and is worth checking out as an alternative to JsTestDriver.

The framework

Image may be NSFW.
Clik here to view.
The Jasmine logo
We’ve picked out Jasmine because it is a mature (compared to most other frameworks) and a popular testing framework for JS. It features BDD syntax, which gives clear and readable tests with functionality in focus. It does not depend on any other framework and does not require a DOM. It can be run in the browser, through JsTestDriver or through node.js. It is well documented and is easy to get started with.

There are a few things to consider. They don’t separate between mocks, stubs and spies. All of that is packed into what Jasmine describes as a spy. The bundled matchers are good, but few. To get readable tests, it’s often necessary to write custom matchers to match the domain language. Still, we consider this one of the few viable options for doing TDD in JS.

Installing, setting up and using Jasmine is easy, and will not be detailed here. We’re using Jasmine through JsTestDriver, and have configured it thereafter.

QUnit was considered as an option, but it’s syntax did not help us achieve as readable and clear tests as Jasmine. It’s simplicity makes it very attractive and useful for beginners, but more advanced users will want more.

Demo application

As stated in the introduction, we’ll make a little application that does an “I feel lucky” search from Flickr. One of the choices we made was to have a minimal setup. No jQuery and third-party libraries in the production code, with the intention to reduce complexity and make the example more transparent as to how things work.

Pre-requisites

If you want to use the same setup as us, we recommend doing the following.
(Or you can check out the entire project from GitHub @ http://github.com/bekk/jstdd).

Download these libraries:

Create the following folder structure:

.
├── lib/ # 3rd party libs lies here
│   ├── jasmine.js
│   ├── jasmine-jstd-adapter/
│   └── JsTestDriver-1.3.2.jar
├── spec/ # test/specs lies here
├── src/  # application source lies here
├── server.sh # Starts a JSTD server (symlinked)
├── test.sh # Runs the test (symlinked)
└── jsTestDriver.conf # JSTD configuration for our project

server.sh and test.sh are just shortcuts to scripts bundled with js-test-driver.

Configure jsTestDriver.conf to match your setup.

Getting familiar with the framework

We started off with a simple test to get familiar with the syntax and to verify that everything is set up correctly. Consider it a warm-up exercise.

describe("Jasmine", function() {
    it("makes testing JavaScript awesome!", function() {
        expect(true).toBeTruthy();
    });
});

Running it

Running it the first time might seem a bit overly hard, but once you’re set up it’s easy to run the tests. Before you start the server, you need to write a little config file. Take a look at ours if you need an example.
Starting the server can seem a bit tedious, but we found a shell-script that starts the server. All we had to do was modify the path to suit our project structure (see server.sh). Running it is just a matter of typing ./server and seconds later the server is up and running.

Example output from a test run:

Running all tests
Chrome: Runner reset.
....
Total 1 tests (Passed: 1; Fails: 0; Errors: 0) (1.00 ms)
Chrome 12.0.742.112 Mac OS: Run 4 tests (Passed: 1; Fails: 0; Errors 0) (1.00 ms)

Next task is capturing browsers. We spun up browsers on each others computers and went to http://[my address]:9876/capture and the browser became a slave. Excellent! We used Safari, Firefox and Chrome to test. Make sure to keep the browser running while testing.

JsTestDriver supports distributed test-running, but that is not something we will explore in this blog post. Now all that is left is telling the server to ask the browsers to run the tests. With the scripts, it’s just a matter of typing ./test in the shell and the tests are run.

The flow for starting the first time is the following:
1. Start server
2. Capture browsers
3. Run tests

After than you just type ./test each time you want to run the tests.

Working on LuckyFlickr

Start with the tests

We decided to start with writing tests to verify output on the result page. The simplest possible test we could think of was to test for a message when no result was found.

Next, we added a new test to verify that if a result were received, the parser would pick the first result and display the image on the result page. Since we were doing unit testing we didn’t want to go all the way to Flickr when we ran the test, so we created a simple stub that returned some JSON data on the expected flickr format so we could run the test in isolation. The specs for the parser read:

  • “outputting result”
    • “should not output an image when there are no results”
    • “should output the image from the result”

Note: When reading these specs, read them like “Outputting result should not output an image when there are no results”.

When pondering what to do next, we figured we could create a test for the component that would fetch data from flickr. Since we wanted to get out data from another site, we decided to use JSONP to overcome the cross-site problem. We also wanted the component to send the flickr request asynchronously, so we had to write a test that would handle that. To fix this we created setup code to temporarily replace the default XMLHttpRequest implementation in JS with our own. That way we could control what the response would be. We decided to let it respond with the same JSON stub as in the parser test.

  • “when querying flickr”
    • “should create a global function that handles the JSON-P callback”
    • “should do a search and call the callback function”

The last step was to create some tests to make sure the GUI was displaying the result in the right container in the result page. On this step, we saw that some of the components came together, and introduced the need for mocking. We mocked the result given by the “fetcher”. Doing so, we’re not dependent on a connection to flickr.com. We created 2 tests for this. The first asserts that the result from flickr is displayed in the result container, and the second one asserts that “Loading” is displayed while it is fetching data from flickr.

The result

Since our focus was unit testing, our implementation ended out with three separate “units” that each did one important thing. We have a “fetcher” that goes out and fetches the result from Flickr, a “result parser” that knows how to parse the JSON data structure from Flickr and finally the component that enables the user to do a search. No enterprisy patterns, no unneeded complexity, no silver bullets. Just enough to make things work. Also, the design emerged from writing the tests first.

Our experiences

We experienced a couple of notable challenges when doing TDD in JS. Doing TDD in it self is not a problem. Some challenges appear when doing browser-specific things with JS. Such as asynchronous callbacks, handling the DOM and the AJAX browser API.

The DOM is an issue. When not using jQuery, there are a lot of quirks that can mess up tests in specific browsers. Also, doing DOM-operations without jQuery results in a lot of ugly code that affects readability.

Testing AJAX without support from the test framework is problematic, and is why several tools exist and mitigate this (Sinon.js, Mockjax, etc.). Before we changed to JSONP, we wanted to do this with no extra support, and ended up with an extremely simplified version of what the mentioned tools provide. But for most people it would be simpler to use an existing tool to help you do AJAX-testing.

But all these things mentioned above are relatively easy to overcome and the upside of being able to write tests for your JS code easily outweighs the small quirks you may experience. And you won’t regret spending time on writing tests when your code base is expanding and you need to make a change.

Give me more!

Sinon.JS – Standalone test spies, stubs and mocks for JavaScript. Works with any unit testing framework.
QUnit – alternative to Jasmine. Developed by the jQuery-team
Cucumber.JS – early stages. Could be good.
Buster.js – a new test framework (currently in beta) from Christan Johansen (the man behind Sinon.js)


Viewing all articles
Browse latest Browse all 7

Trending Articles