Introduction to JavaScript testing

with Jasmine

Slobodan Stojanovic

I am

CTO of Cloud Horizon

JS Belgrade meetup organizer

The goal of this presentation

is to show you:

  • What is unit testing?
  • Test Driven Development?
  • Jasmine?
  • How can these things help me?
  • And how can you use Jasmine?

So, let's start!

Unit testing...

software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation

is just the piece of the code

that tests the correctness

of some (small) piece of your code

Unit testing...

Example:

describe('Addition functionality', function() {
  it('should add 2 numbers', function() {
    expect(add(1, 1)).toBe(2);
  });
});

Or let's use ES6:

describe('Addition functionality', () =>
  it('should add 2 numbers', () =>
    expect(add(1, 1)).toBe(2);
  );
);

Is there any other type?

Yes, of course, ie.

  • Integration test
  • Smoke test (aka Sanity check)
  • ​Regression test
  • Acceptance test
  • ...
     

A bit more detailed answer:

http://stackoverflow.com/a/520116

Test Driven Development (TDD)

software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only

TDD flow:

  • (Red) Write your test first, and see it fail.
  • (Green) Write your unit of code and see the test pass.
  • (Refactor) Make your code better, using the tests to keep you safe.

But what about BDD?

Behaviour-Driven Development (BDD) is an evolution in the thinking behind TDD.
 

It brings TDD and Domain Driven Design into an integrated whole, making the relationship between these 2 approaches more evident.

 

It aims to help focus development on the delivery of prioritised, verifiable business value by providing a common vocabulary.

Core principles of BDD:

  1. Business and Technology should refer to the same system in the same way

  2. Any system should have an identified, verifiable value to the business

  3. Up-front analysis, design and planning all have a diminishing return

BDD relies on the use of a very specific (and small) vocabulary to minimise miscommunication and to ensure that everyone – the business, developers, testers, analysts and managers – are not only on the same page but using the same words.

Jasmine is...

behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.

- BDD framework for testing JavaScript code

Because we are writing our tests more as specifications that describe behaviour

- It does not depend on any other JavaScript frameworks and it does not require a DOM

So we can use it to test browser JavaScript, node.js or any other JavaScript code

- And it has a clean, obvious syntax so that you can easily write tests.

Let's see

Syntax:

describe('Addition functionality', () =>
  it('should add 2 numbers', () =>
    expect(add(1, 1)).toBe(2);
  );
);

A bit more syntax...

Let's split the syntax in 3 groups:

  • statements
  • matchers
  • double­s/spies                                                        

 

Cheatsheet:
https://www.cheatography.com/citguy/cheat-sheets/jasmine-js-testing/

Statements

  • desc­ribe( label, functi­on(){ ... } )
  • it( label, functi­on(){ ... } )
  • expe­ct( actual )
  • befo­reE­ach­(­ fun­cti­on(){ ... } )
  • afte­rEa­ch­( f­unc­tion() { ... } )
  • ...

Matchers

  • to(N­ot)­Be( null | true | false )
  • to(N­ot)­Equ­al( value )
  • to(N­ot)­Mat­ch( regex | string )
  • toBe­Def­ine­d()
  • toBe­Und­efi­ned()
  • toBe­Nul­l()
  • toBe­Tru­thy()
  • toBe­Fal­sy()
  • ...

Double­s/Spies

  • spyOn( obj, method­_st­ring )
  • toHa­veB­een­Cal­led()
  • toHa­veB­een­Cal­led­With( array )
  • andC­all­Thr­oug­h()
  • andR­etu­rn( value )
  • andC­all­Fak­e­( fu­nct­ion() { ... } )
  • ...

What a Jasmine Suite Looks Like

  • At least one describe block (they can be nested)
  • At least one it block which contains a spec/test
  • At least one expectation, which consists of:
    - expect which is passed an expression (called the "actual" value)
    - a matcher with the expected value (toBe and toBeGreaterThan)

Example:

describe('Calculator', () =>
  describe('Addition function', () =>
    it('should add numbers', () => {
      expect(add(1, 1)).toBe(2);
      expect(add(2, 2)).toBeGreaterThan(3);
    });
  );
);

But theory is boring, let's run the example!

Add this to some html file:

<link rel="stylesheet" type="text/css"
      href="libs/jasmine/2.4.1/jasmine.css">
<script type="text/javascript"
        src="libs/jasmine/2.4.1/jasmine.js"></script>
<script type="text/javascript"
        src="libs/jasmine/2.4.1/jasmine-html.js"></script>
<script type="text/javascript"
        src="libs/jasmine/2.4.1/boot.js"></script>

<!-- include source files here... -->
<script type="text/javascript"
        src="src/calculator.js"></script>

<!-- include spec files here... -->
<script type="text/javascript"
        src="spec/calculator-spec.js"></script>

calculator-spec.js file:

describe('Calculator', function() {
  describe('Addition function', function() {
    it('should add numbers', function() {
      expect(add(1, 1)).toBe(2);
      expect(add(2, 2)).toBeGreaterThan(3);
    });
  });
});

Let's use our calculator test example.
And let's keep calculator.js empty.

Now let's run html file

And... We have our first test 🎉
It's failing, but that's ok (remember TDD flow)

Let's fix it

Add simple function to src/calculator.js and run it again

function add() {
  return false;
}

Now let's add a functionality and try again

function add(a, b) {
  return a + b;
}

🎉🎉🎉

Our test is passing now!

Ok, new errors:

Ok, this is nice, but...

I know, basic example is not that useful, let's see what else Jasmine can do

Spies

Jasmine has test double functions called spies.

 

A spy can stub any function and tracks calls to it and all arguments.

 

A spy only exists in the describe or it block in which it is defined, and will be removed after each spec

Creating a spy

// Spy on an existing method
spyOn(obj, 'method');

// Create a new function to use as a spy
jasmine.createSpy('optional name');

// Create a new object with spy functions as properties
jasmine.createSpyObj('name', ['fn1', 'fn2', ...]);

Modify behavior when the spy is called

// Call through to the original
obj.method.and.callThrough();

// Return the specified value
obj.method.and.returnValue(val);

// Call the given function instead of the real one
obj.method.and.callFake(function() {...});

// Throw an error
obj.method.and.throwError(err);

// Reset to default stubbing behavior (resets the operations above)
obj.method.and.stub();

Verifying and counting calls on a spy

// Returns true if any calls have been made
obj.method.calls.any();

// Returns the number of times the spy got called
obj.method.calls.count();

// Reset the call tracker
obj.method.calls.reset();

// Returns the first call’s context and arguments
obj.method.calls.first();

// Returns the most recent call’s context and arguments
obj.method.calls.mostRecent();

// Returns array of context and arguments passed to each call
obj.method.calls.all();

Custom matchers

You can also add your custom matchers:

jasmine.addMatchers({
  toBeDivisibleByTwo: function () {
    return {
      compare: function (actual, expected) {
        return {
          pass: (actual % 2) === 0
        };
      }
    };
  }
});

Plugins

There's also a lot of custom matchers published as plugins, ie:
 

Jasmine jQuery

Provides matchers for jQuery objects, and an easy way to build HTML fixtures to test with.


AngularJS Matchers

Provides matchers for working with the Angular JS framework.

jasmine.any

A helper utility that lets you match against a constructor or "class".

expect(date).toBe(jasmine.any(Date));

and more...

Why and how are testing and Jasmine useful for you

Now let's talk about
the most important thing:

Introduction to JavaScript testing with Jasmine

By Slobodan Stojanovic

Introduction to JavaScript testing with Jasmine

  • 1,252