Chapter 3. Writing Good Tests

So, now you know how to write tests with Jasmine. In theory, you could write an infinite number of tests for your code, testing weird conditions and more, but you don’t have unlimited time on your hands. You have to write the correct specs for the job.

This is subjective; none of this is gospel by any means. These are simply my recommendations after having worked with Jasmine on a number of projects.

Cardinal Rule: When in Doubt, Test

If you’re not sure whether or not to test something, it probably doesn’t hurt to test it. If it would take you, as the developer, a long time to develop the spec, you might want to make sure you really need to build it. If it’d make Jasmine run slowly (perhaps it’s doing a large computation), you might also want to reconsider. Usually, specs are pretty short and pretty speedy, so if you’re not sure, make a spec!

Test Components

Test individual components of your code, rather than everything at once. For example, if you have a Calculator class, you don’t want to test it like this:

  describe("calculator addition", function() {
      it("can add, subtract, multiply, and divide positive integers",
         function() {
          var calc = new Calculator;
          expect(calc.add(2, 3)).toEqual(5);
          expect(calc.sub(8, 5)).toEqual(3);
          expect(calc.mult(4, 3)).toEqual(12);
          expect(calc.div(12, 4)).toEqual(3);
      });
  });

That large spec should be split up into four different specs, because you’re really testing four different parts. This is a step in the right direction:

  describe("calculator addition", function() {
      var calc;
      beforeEach(function() {
          calc = new Calculator();
      });
      it("can add positive integers", function() {
          expect(calc.add(2, 3)).toEqual(5);
      });
      it("can subtract positive integers", function() {
          expect(calc.sub(8, 5)).toEqual(3);
      });
      it("can multiply positive integers", function() {
          expect(calc.mult(4, 3)).toEqual(12);
      });
      it("can divide positive integers", function() {
          expect(calc.div(12, 4)).toEqual(3);
      });
  });

Each spec should test only one case or scenario at a time. In the previous example, if you had an error in your mult function, the spec would fail even if the other components worked perfectly. In this example, only one test will fail, and you’ll be able to more quickly pinpoint that your multiplication is broken.

Black-Box Testing

When writing behavior-focused tests, you can imagine your software being a black box. You care only about the software’s behavior, not what happens internally.

A simple example: if your person object has a function that includes a private method (not technically private, sorry) called _generateHello, it might look like this when calling helloWorld:

  var person = {
      // Private method
      _generateHello: function() {
          return "hello";
      },
      // Public method
      helloWorld: function() {
          return this._generateHello() + " world";
      }
  };

Because _generateHello is a private method, you’d never test that in Jasmine. You don’t need to, because you don’t care how it works. You just care how the public method works.