English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Developing a Backbone.js Edge

Chapter 2: Test Driven Development

Imagine you are in charge of building an airplane. Would you simply create all of the various airplane parts, put them together, and then hope that the plane flies? Of course not! You'd set up strict testing requirements and quality assurance guidelines for each part individually as well as the integration of the parts. Think about software in the same way: each part must be tested, both separately, and together, to know if it works properly.

Most people start out by testing their code manually. This approach usually means writing some JavaScript, loading it into the browser, and manually trying out the feature to see if it runs without error and does what you expect. If it works, you're happy and move on. If it doesn't then you keep trying new things until it does.

The Problem

The problem with this typical manual testing approach is that a feature that works properly today may accidentally break when you add or change more code later. Often times you may break something that you didn't even know was related to what you were working on. The larger an application gets, the more things can go wrong, and it becomes increasingly difficult to manually test all of the parts of your application.

When you originally develop a feature, you may know all the little edge cases to test out. But can you guarantee you'll remember in six or twelve months when you need to tweak the feature? Or perhaps even more importantly: do you trust that all of your current and future teammates will also remember to properly test all of those edge cases? 

The Solution

The solution is to add in programmatic testing. Unit tests are little pieces of code that test a specific part of your application code. Rather than manually clicking around in your application to test some code, you write test code, to test it for you. The tests are usually kept in source control alongside your application code itself, and should run (making sure all tests pass) before a version of the software is considered ready for production.

Our Approach

You'll notice we sprinkle testing throughout the entire book, rather than dumping testing into a chapter at the end. The reason for this is twofold:

1. Test-driven Development (TDD)

It makes more sense to write testing code before or during writing functional code, rather than after, so that your tests actually help you complete your task rather than being a chore. There are entire books written on Test-driven Development, but the basic idea is simple:

  1. Write new tests that fail initially, but that demonstrate how a new feature or bug fix should work once completed.
  2. Write your code so that all tests (existing and new) pass.
  3. Rinse and repeat!

If you can get in a habit of testing in this manner, your project's codebase will be much more robust and unlikely to break in future releases.

2. Clarity and Understanding

Rather than show you some Backbone.js code and then try to explain how it works and what its effect is, a unit test can explicitly prove what code does. It's the difference between describing what should happen and showing exactly what does happen.

Consider this approach in your own programming as well, especially when you're trying to fix a hard-to-track-down bug. When it seems like everything should be working but isn't, what we like to do is start writing tests to prove that certain parts are behaving as we expect them to. A lot of times we discover that a part of our application that we thought worked properly doesn't.

At other times, you may discover that an underlying framework (such as Backbone.js) either has a bug or doesn't work exactly how you thought it did. This provides opportunities to "give back" to the Backbone project by creating a Pull Request on GitHub showing exactly how to reproduce the problem, and ideally a way to fix the bug itself. And the best part is, by contributing a unit test with the use case you care about, you can feel more confident knowing that this behavior won't accidentally change in a future version of the project.

Written tests also help when working with a team, or even when asking for help online on StackOverflow, in mailing lists, or in GitHub Issues. When you can write a straightforward unit test showing what a problem is, then you've given others a reproducible way to see the problem and understand it in as little code as possible. We groan every time we see a question online with way too much (irrelevant) information and no easy way to reproduce a version of the problem. But when you see somebody show up with a unit test demonstrating their exact problem, it becomes much easier to a) fix a bug in the underlying code they are relying on, or b) see what they are doing wrong themselves.

Writing Tests

We are using QUnit as a testing framework in this book and in the example code. We've chosen QUnit because it's very popular and powerful, and because the tests for Backbone.js itself use QUnit. 

A test can be very simple. Here's a test that simply demonstrates that JavaScript allows you to get the length of a list of numbers.  Let's make a file to hold our general tests.

// test/generic.js
window.jQuery(function(){
module('Generic');
 
test("Length of an array", function() {
var myList = [1, 3, 5, 7];
equal(myList.length, 4, 'An array should have a .length attribute');
});
});
 
// test/index.html
<head>
...
<!-- load the generic test file -->
<script src="generic.js"></script>
</head>

Then, just open test/index.html in your browser to run it.  As you can see, testing can be easy. QUnit just assumes that you wrap your test functions in a function called test() and give them a name, and that you use special assertion functions like equal(), notEqual(), and ok() to check if code is working properly.

You'll learn more about Backbone models in Chapter 5: Models, but here's an example that will show both the basics of how a model can work to store data and an example of another test.  Add this directly under the last test:

// test/generic.js
test("A Backbone Model should store data", function() {
// Create an instance of a generic Backbone Model.
var model = new Backbone.Model();

// Store some data in it.
model.set('first_name', 'John');
model.set('last_name', 'Smith');

// Test that we can retrieve the data from it correctly.
equal(model.get('first_name'), 'John', 'can retrieve first_name
correctly');
equal(model.get('last_name'), 'Smith', 'can retrieve last_name
correctly');
});

Once you understand the basics of testing, you'll want to be writing unit tests that test your own application's code, rather than testing JavaScript Arrays or the basics of Backbone.

Running Tests

JavaScript tests can be run very easily from the browser. In our project, just open test/index.html in your web browser and you'll see a nicely formatted page running the tests. You can switch to the complete git branch to see the tests for the completed Hubbub project, or just view them online here.

If all of the tests passed, you'll see a green strip at the top of the page, as shown in this figure:

If any tests are failing, you'll see a red strip with details about the failing test, as shown here:

As you write code for your application, be sure to check your tests frequently.

To get more comfortable with reading and writing JavaScript unit tests, just open up test/index.js in a code editor. You can read through some of the existing Hubbub tests (don't worry if they don't make a lot of sense yet; they will make more sense by the end of the book). You can also practice writing your own tests. To start, write out the "Length of an array" test above. Then re-run the tests in your web browser to see your new test pass.

Best Practices

There are an abundance of books on how to write good software tests, but here are a few best practices to get you started:

  • Make each test independent from others. Testing frameworks have a way to explicitly define what set-up work a test needs (see how in QUnit), so make sure you're not expecting one test to do anything that another test requires in order to pass. This means your unit tests should be able to be run in any order.
  • A unit test should test as little code as possible. While there are advantages to writing integration tests that test your overall application, the most common type of test should be testing one specific module of code and focusing only on that one piece.
  • A test should be as short as possible and have one purpose. Define one purpose for your test and don't add in a bunch of unrelated assertions. 

Backbone's Own Tests

A great way to learn Backbone.js in more depth is to look at the tests for the library itself. They can be viewed on GitHub, and run on the Backbone site. If you have a question about the specifics of how Backbone.js works in some detailed case where the documentation isn't clear, just write a test to prove to yourself one way or another.

Here's one little example. How does Backbone's Collection#slice method work? We could read the documentation of course, but here is a test for it:

// test/generic.js
test("slice", 2, function() {
var col = new
Backbone.Collection([{a: 'a'}, {b: 'b'}, {c: 'c'}]);
var array = col.slice(1, 3);
equal(array.length, 2);
equal(array[0].get('b'), 'b');
});

Advanced Testing Tips

Once you're comfortable with writing and running tests for your application, you'll want to learn about more advanced testing methods. Here are a few resources to help. (But don't worry about these yet, if you're just starting out with testing.)

Running Tests: You can use a tool like PhantomJS to run your tests (and see if they pass) from the command line. This allows you to easily integrate with continuous integration frameworks. You can use a service like CircleCi or Travis CI in such a way that every time you commit new code, it is automatically tested, and then deployed to a production server if the tests pass.

Mocks, Spies, and Stubs: Sinon.JS is also helpful in testing (with QUnit or by itself) in many ways, but especially in testing code that relies on communicating with a server via AJAX/XMLHttpRequest. You can mock out server responses with a fake server to test that your response handling code is correct, and you can test that the AJAX requests your code is generating is correct. See an example here of using these concepts to test how a Backbone Model syncs with a server.

Other Testing Frameworks: Our project and Backbone.js itself uses QUnit as a testing framework, but you may want to check out alternative testing frameworks like Jasmine and Mocha

Conclusion

The most important unit test in your application is its very first one. Once you're past the fear that a lot of beginners have regarding testing and have a few example tests for your application in place, the rest is relatively easy. If your experience is anything like ours has been, it will make your life much easier later to help keep things from accidentally breaking. In the next chapter it is time to learn about Events in Backbone.

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.