Adding Jasmine Tests to your project

Projected Time

60 minutes

Prerequisites

Here are links to lessons that should be completed before this lesson:

Motivation

Learn a commonly used testing tool.

Jasmine is a Behavior-Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it’s suited for websites, Node.js projects, or anywhere that JavaScript can run.(stackshare.io)

Which companies use Jasmine testing?

Objectives

Participants will be able to:

Specific Things to Learn

Materials

Lesson

Jasmine Test

Jasmine is a behavior-driven development (BDD) framework for testing JavaScript code. (Note: BDD is a specific style of testing that tests the behavior of the code from the user’s perspective, rather than testing implementation details. It is often used with TDD! Learn more.) Jasmine has no external dependencies and does not require a DOM, which means that it’s good for getting tests up and running quickly.

As we learned in the last lesson on test-driven development (TDD), one way to ensure that your code is well-tested is to start by writing a test for the behavior you want, watch it fail, and finally write the code to make it pass (the Red-Green-Refactor pattern). Though working in a TDD style may feel slower at first, it can save you time in the long run by ensuring that your code won’t break. We’ll be working in a TDD style through this lesson.

Let’s get started by setting up a new project with Jasmine tests.

Create a new project

Install Jasmine

Initialize Project

Create Files

Start Test

Started


No specs found
Finished in 0.004 seconds
Incomplete: No specs found

Jasmine Syntax

describe('A string', function() {
  it('containing 4 letters should have length 4', function() {
    WORD = 'word';
    expect(WORD.length == 4).toBe(true);
  });
});
Started
.


1 spec, 0 failures
Finished in 0.006 seconds
Started
F

Failures:
1) A string containing 4 letters should have length 4
  Message:
    Expected false to be true.
  Stack:
    Error: Expected false to be true.
        at <Jasmine>
        at UserContext.<anonymous> (/path_to_project/jasmine-practice/spec/string.spec.js:4:34)
        at <Jasmine>

1 spec, 1 failure

Adding more tests

describe('A string', function() {
  it('containing 4 letters should have length 4', function() {
    WORD = 'word';

    expect(WORD.length == 4).toBe(true);
  });

  // New spec!
  it('should be equal to an identical string', function() {
    WORD = 'word';

    expect(WORD == 'word').toBe(true);
  });
});
describe('A string', function() {
  let WORD = 'word';

  it('containing 4 letters should have length 4', function() {
    expect(WORD.length == 4).toBe(true);
  });

  it('should be equal to an identical string', function() {
    expect(WORD == 'word').toBe(true);
  });
});

Other matchers

describe('A string', function() {
  let WORD = 'word';

  // ... previous tests

  // New test
  it('should be more than 5 characters long', function() {
    expect(WORD.length).toBeGreaterThan(5);
  });
});
Failures:
1) A string should have a length greater than 5
  Message:
    Expected 4 to be greater than 5.
  Stack:
    Error: Expected 4 to be greater than 5.
        at <Jasmine>
        at UserContext.<anonymous> (/Users/brookeangel/Code/jasmine-practice/spec/string.spec.js:13:27)
        at <Jasmine>
describe('A string', function() {
  // ... previous tests

  it('should have a length greater than 5', function() {
    expect('elephant'.length).toBeGreaterThan(5);
  });
});

Including modules in Jasmine tests

// src/myFunction.js
function myFunction() {}

module.exports = myFunction;

// spec/myFunction.spec.js
const myFunction = require('../src/myFunction');

Local Installation

Common Mistakes / Misconceptions

  1. Remember: you can get “false positives” and “false negatives” in tests. That’s why it’s good to follow a Red-Green-Refactor pattern, and make sure that your tests fail before implementing the code to make them pass.
  2. A test with no expectations in it will pass. Don’t forget to add at least one expect to every it function, or you could end up with this false positive.
  3. Pay attention to when you are writing tests for Asynchronous code. The testing engine might complete before asynchronous code has completed running, giving you unreliable tests.
  4. expect inside of asynchronous code is ignored, therefore passing. (false positive)

Guided Practice

Remember the Basic JavaScript practice that we completed a few lessons ago? We’re going to rewrite a few of those functions, TDD style!

FizzBuzz (from basic JS practice, challenge 4)

We’re going to TDD a slight twist on fizzBuzz. The function will:

  1. Within your existing project, create a new test file in the /spec directory named fizzBuzz.spec.js. Our tests will go here.
  2. Within fizzBuzz.spec.js, add a describe to add some context.
  3. Let’s add our first test: test that fizzBuzz is defined.
  4. Run the test using npm test. If all goes well, you should see the following failure:
1) fizzBuzz should be defined
  Message:
    ReferenceError: fizzBuzz is not defined
Check your work so far

// spec/fizzBuzz.spec.js
describe("fizzBuzz", function(){
  it("should be defined", function(){
    expect(fizzBuzz).toBeDefined();
  });
});

  1. Let’s make the test pass! Create a new directory /src in the project, and make a fizzBuzz.js file within the directory. Add a fizzBuzz function to the file, and export it from the file so that we can import it in our test. Don’t add any functionality to fizzBuzz just yet! All that our function has to do to make the test pass is “be defined”.
  2. Now, import your fizzBuzz function into the spec file. (Hint: see the “Including modules in Jasmine tests” section above.)
  3. The first test should now pass!
    Check your work so far
     // src/fizzBuzz.js function fizzBuzz() {};

module.exports = fizzBuzz;

// spec/fizzBuzz.spec.js describe(“fizzBuzz”, function(){ it(“should be defined”, function(){ expect(fizzBuzz).toBeDefined(); }); });
  1. Great! Our test is passing. Add another test that checks that calling fizzBuzz with a multiple of 3 returns “fizz”. Make sure that your test fails.
  2. Now, make your test pass in the simplest way possible by adding functionality to the fizzBuzz function.
    Check your work so far
     // src/fizzBuzz.js function fizzBuzz(num) { return “fizz”; };

module.exports = fizzBuzz;

// spec/fizzBuzz.spec.js describe(“fizzBuzz”, function(){ // older tests

it(“should return ‘fizz’ when given a multiple of 3”, function(){ expect(fizzBuzz(3)).toBe(“fizz”); expect(fizzBuzz(6)).toBe(“fizz”); }); });

Notice that we haven’t implemented all the functionality for fizzBuzz yet - we don’t have to for the test to pass. That means we should add more tests!

  1. Let’s add the complete functionality for fizzBuzz.
Check your work

// src/fizzBuzz.js
function fizzBuzz(num) {
  if (num % 15 === 0) {
    return "fizzbuzz";
  } else if (num % 3 === 0) {
    return "fizz";
  } else if (num % 5 === 0) {
    return "buzz";
  } else {
    return num;
  }
};

module.exports = fizzBuzz;

// spec/fizzBuzz.spec.js
const fizzBuzz = require('../src/fizzBuzz');

describe("fizzBuzz", function(){
it("should be defined", function(){
expect(fizzBuzz).toBeDefined();
});

it("should return 'fizz' when given a multiple of 3", function(){
expect(fizzBuzz(3)).toBe("fizz");
expect(fizzBuzz(6)).toBe("fizz");
});

it("should return 'buzz' when given a multiple of 5", function(){
expect(fizzBuzz(5)).toBe("buzz");
expect(fizzBuzz(10)).toBe("buzz");
});

it("should return 'fizzbuzz' when given a multiple of 3 and 5", function(){
expect(fizzBuzz(15)).toBe("fizzbuzz");
expect(fizzBuzz(30)).toBe("fizzbuzz");
});
});

Independent Practice

Exploring new matchers:

  1. Re-implement Challenge 6, “Sleeping In”, from the basic JavaScript practice linked above, using TDD. Since this function returns a boolean, use the matchers toBeTruthy and toBeFalsy in your tests.
  2. Write a function mySplit that takes a string, and returns an array of its letters, using TDD. Use the matcher toContain in your test.

Challenge

Challenge 1: Read the docs on beforeEach and afterEach. Try to write a test that uses beforeEach to set up tests. There’s a good example in the Jasmine Tutorial.

Challenge 2: You can also run Jasmine tests in the browser for a nicer UI! Follow the installation instructions on Jasmine’s github repo to make your tests run in the browser instead of the command line.

Supplemental Materials

Check for Understanding

Question: What is Jasmine and how does it work in your project?

Exercise: Each student should pick a matcher, like toBeNull(). Then describe that matcher to the class and how it should be used.