Introduction to Unit Testing

1. AN INTRODUCTION TO UNIT TESTING

If you’re coming to jQuery and JavaScript engineering from some other programming discipline, the concept of unit testing is likely familiar to you. If that’s the case, or you’re among the minority of front-end engineers already doing unit testing, feel free to skip ahead to the sections that deal specifically with unit testing with QUnit. Otherwise, read on and learn about the benefits of a development approach centered on a structured, atomic testing framework.

As a note, this is an area of front-end engineering where there’s an opportunity to get ahead of the curve. As the DailyJS JavaScript Developer Survey 2011 Results (http://dailyjs .com/2011/12/15/javascript-survey-results/) show, only 42 percent of JavaScript engineers are unit testing their code. That number is up from 2010, but it still shows that there’s a gap between the tools commonly used in other programming disciplines and the ones used in the average JavaScript project.

As you’ll soon see, the benefits are potentially significant, so ramping up to include unit testing in your development toolbox is a worthwhile effort.

2. WHAT IS UNIT TESTING?

Unit testing is a software practice where individual pieces of code (called units) are tested to determine their viability for a specific task. Units should be as small as possible, have one or a few inputs, and a single output.

Unit tests are generally automated, either as part of a build process, or as a pre-commit hook for check-ins or some other step of the software life cycle. Each test returns a binary pass/fail result.

Unit testing is a vital partner of a style of testing much more familiar to JavaScript developers: functional testing. Functional testing, what most people think of as “QA” or, more formally, acceptance testing, tests the site or application from the end user’s perspective, after it’s complete. Unit tests flip that model on its head. Unit tests are written alongside the code they’re meant to test and are, most importantly, written from the developer’s perspective.

Instead of testing the result, an end user will see unit tests analyze the expected result, from a software perspective, of a piece of code.

2.1. Benefits of Unit Testing

Some of the benefits of unit testing should be obvious. Testing code early and often improves code quality by uncovering issues that might be glossed over with simple, unstructured developer tests. Better code quality means fewer bugs during functional testing or, worse, in production.

“Fewer bugs” should make everyone involved with your project happy, including your users.

Beyond producing better code at the start of a project, one other key use of unit testing is automated regression testing. This is one of the most important uses of unit testing in the jQuery project itself. Using automated unit tests ensures that new features and/or bug fixes don’t introduce other errors. This is a key to ensuring the stability of the library.

Rick Waldron’s line in the jQuery Bug Fixing Guide (http://weblog.bocoup.com/javascript- jquery-bug-fixing-guide) bellows (in all caps) the importance of this with the admonition “ALWAYS RUN THE FULL SUITE BEFORE COMMITTING AND PUSHING A PATCH!!!” That’s clarity.

Running the full test suite and ensuring all tests still pass means you’ve added to, but not subtracted from, the library as a whole.

The unit testing structure allows for easier refactoring. Because you know exactly what tests a particular piece of code needs to pass, you can more confidently refactor that code without the nagging fear that you’re missing some dependency or other. No developer wants to feel the fear that the code she or he has rewritten will bring the whole application crashing down. Passing existing unit tests alleviates that fear significantly.

Additionally, because good unit testing relies on testing standalone modules, coding for unit testing forces developers to write more loosely coupled code. You want to avoid writing tests that spread across more than one module, so you’re forced to write more abstract interfaces for inputs and outputs that can more easily be mocked up in a testing framework.

Coding for standalone unit tests also means that code can be tested and verified well before other, dependent systems are complete. With a well-documented interface and well-written unit tests, modules can be coded confidently even if other modules are to be created later on in the process. This can be especially beneficial with large teams spread across different organizations.

A consultant can come in, create a module for which he’s specifically suited, and if his code is well tested and works properly with the agreed-upon interface, he could theoretically move on to his next gig, well before the rest of the organization catches up to his progress. This doesn’t negate the need for integration tests (tests to ensure that the actual systems involved function as expected when coupled together), but it should alleviate many of the surprises that can pop up with a less structured approach to development.

2.2. Test-Driven Development

Unit testing is the foundation of an advanced software methodology called Test-Driven Development (TDD). TDD encourages extremely short development cycles by centering the development life cycle on small unit tests. A developer will start by writing a failing unit test. He’ll then write the minimum amount of code to pass that failing test. Finally, he’ll move on to the next feature and/or refactor the code for better quality. Although this chapter doesn’t go into specific TDD practices, TDD is often mentioned alongside unit testing, so the relationship is important to point out.

Basically, though you can’t do TDD without unit tests, you can unit test without signing up to practice the full spectrum of TDD.

2.3. Understanding What Makes a Good Unit Test

Now that you know about the basics of unit testing, it’s important to get a sense of how to create a quality unit test.

There are several hallmarks of a good unit test. The following list outlines some tenets to follow. In general, unit tests should be:

  • Automated: Getting developers to do anything is hard enough as it is. Testing has to be as easy as possible or else there won’t be buy-in from the front lines.
  • Fast: Anything else and developers are going to be less likely to get with the testing program.
  • Focused: They should test as narrow a component as is possible to cut down on ambiguity.
  • Independent: This can be approached from two different angles. Both are important:
    • Self-contained: Tests need to be atomic. This means that you may need to use some code to stand in for external inputs/outputs. This code is known in testing circles as “mocks” or “stubs.” You see more about mocks later in the chapter.
    • Standalone: In addition to having no external dependencies, unit tests should be able to be run in any order, without having sequential dependencies on any other test.
  • Consistent: Tests should always return the same results.
  • Readable: Tests should be clear in intent and focus so that the developers that follow you onto a project will be able to pick up on the purpose of your tests without so much ramp-up time.
  • Maintainable: Unit tests will live with your project, so they should be maintainable. Having out-of-date tests is better than having no tests at all, but out-of-date tests will cause their own problems.
  • Trustworthy: Developers should have complete confidence in the result of a unit test.

Now that you’ve got a handle on unit testing in general, it’s time to take a look at the star of the show, QUnit.

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *