Puzzle Frontend Kafi #9, 23.11.2017

Mathis Hofer, hofer@puzzle.ch

Jest

Goals

What is Jest and how can I use it?

How does snapshot testing work?

Introduction

License

Previously: BSD + Patents

Now: MIT

Advantages

Easy Setup

Instant Feedback

Snapshot Testing

 

No browser needed (Node/jsdom)

Parallelized

No webpack needed

 

Who uses it?

Basic testing

const sum = require('./sum');

describe('sum', () => {

  beforeEach(() => ...);

  afterEach(() => ...);

  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });

});

test also under alias it

describe.only/test.only (alias fdescribe/fit)

describe.skip/test.skip (alias xdescribe/xit)

Structure and scoping

Matchers*

expect(result).toBe(obj)
expect(result).not.toEqual('foo')
expect(result).toBeDefined()
expect(result).toBeNull()
expect(result).toBeTruthy()

expect(getAllFlavors())
  .toContain('lime');

expect(essayOnTheBestFlavor())
  .toMatch(/grapefruit/);

expect(drinkOctopus)
  .toThrowError(DisgustingFlavorError);

*mostly Jasmine compatible

Asynchronous

it('works with function argument', (done) => {
  user.getUserName(4).then(data => {
    expect(data).toEqual('Mark');
    done();
  });
});

it('works with promises', () => {
  expect.assertions(1);
  return user.getUserName(4).then(data => expect(data).toEqual('Mark'));
});

it('works with resolves', () => {
  expect.assertions(1);
  return expect(user.getUserName(5)).resolves.toEqual('Paul');
});

it('works with async/await', async () => {
  expect.assertions(1);
  const data = await user.getUserName(4);
  expect(data).toEqual('Mark');
});

Running

# Run for (/__tests__/.*|\.(test|spec))\.(ts|js)$
jest

# Run continously only for changing files
jest --watch

# Run for files changed since last commit
jest -o

# Run on files changed in last commit
jest --lastCommit

# Run matching spec name
jest -t 'displays error on complete import failure'

# Run tests related to given files
jest --findRelatedTests path/to/file.js

Debugging

Run Jest as such:

node --inspect-brk node_modules/.bin/jest --runInBand [other arguments here]

Open displayed link in Chrome

Mocking

Mock functions

let mockFunc = jest.fn();

mockFunc
  .mockReturnValueOnce(10)
  .mockReturnValueOnce('x')
  .mockReturnValue(true);

mockFunc = jest.fn((a, b) => a + b);

expect(mockFunc).toBeCalled();
expect(mockFunc).toBeCalledWith(arg1, arg2);

expect(mockFunc.mock.calls.length).toEqual(2)

Spies

const spy = jest.spyOn(video, 'play');
video.play();
expect(spy).toHaveBeenCalled();

Mock modules

import importHelper from '../helpers/import';

jest.mock('../helpers/import', () => ({
  importTabGroupsJson: jest.fn().mockImplementation(() => Promise.resolve()),
}));

Mock timers

jest.useFakeTimers();

jest.advanceTimersByTime(1000);

jest.runAllTimers();

Manual mocks

Put file in __mocks__/fs.js

In test do: jest.mock('fs');
 

Snapshot testing

Demo

But... what about TDD?

Snapshots help figuring out whether the output of the modules covered by tests is changed, rather than giving guidance to design the code in the first place.

Jest + Angular?

Yes, please!

Setup

Add TypeScript support

Install jest-preset-angular

 

Add file setupJest.ts:

"jest": {
  "preset": "jest-preset-angular",
  "setupTestFrameworkScriptFile": "./setupJest.ts"
}

Add this to package.json:

import 'jest-preset-angular';

Demo

Further topics

Babel

Add .babelrc

Install babel-jest

TypeScript

Install ts-jest

Configure transformer for *.ts in package.json

Coverage

jest --coverage
jest --coverage --coverageDirectory=reports/coverage

JUnit XML

Install jest-junit

Set it as testResultsProcessor

Out of the box:

Migration

Codemods for Jasmine, Mocha, Chai etc.:

https://github.com/skovhus/jest-codemods

(based on jscodeshift)

Done.

JavaScript Testing with Jest

By Mathis Hofer

JavaScript Testing with Jest

  • 1,380