JavaScript Unit tests
This article is about writing unit tests for non component stuff(models, reducers, actions, selectors).
Rules:
- Adhere to theĀ F.I.R.S.T. principles
- Avoid complicated logic. Try to keep tests as simple as possible
- Use beforeEach for common logic for 2 or more test cases
- 1 expect per 1 test case(it)
- Place, variables inside describe section
- Indicate the type of the tested file
Test template
/ [START] describe global variables / /* global describe, it */ / [END] describe global variables / / [START] import vendors / import { expect } from 'chai'; / [END] import vendors / / [START] import common stuff / import { isVersionCompatible, compareVersions } from 'src/tests/helpers'; / [END] import common stuff / / [START] import local stuff / import { getEntities, } from './model'; / [END] import local stuff / / [START] describe 1 level / describe('(Type) mergeAttributes', () => { / [START] variables for describe 1 level / const existing = { description: 'description' }; / [END] variables for describe 1 level / / [START] optional logic to be performed before each test case / beforeEach(() => {}); / [END] optional logic to be performed before each test case / / [START] tests for describe 1 level / it('should replace existing data with incoming data', () => { / [START] variables for test / const incoming = { description: 'no, this description', isSelected: true }; / [END] variables for test / const actual = mergeAttributes(existing, incoming).description; const expected = 'no, this description'; expect(actual).to.equal(expected); }); / [END] tests for describe 1 level / / [START] describe 2 level / describe('merging existing data with incomplete incoming data', () { / [START] variables for describe 2 level [END] / / [START] tests for describe 2 level / / ... / / [END] tests for describe 2 level / }); / [END] describe 2 level / / Other describe 2 level / }); / [END] describe 1 level /
Synthetic example to show how to mock external dependencies
Model file
An example of model file which generates book samples
import createBookSample from 'core'; const generateBookSamplesCreator = (createBook) => ({quantity, publisher}) => { let currentQuantity = quantity; let books = []; while (currentQuantity !== 0) { books.push(createBook({publisher})); currentQuantity--; } return books; }; export const generateBookSamples = generateBookSamplesCreator(createBookSample);
Unit test for above-named file
Unit test for above-named file
import sinon from 'sinon'; import { generateBookSamplesCreator } from './generateBookSamples'; describe('(Model) generateBookSamples', () => { const book = { title: 'random generated title', description: 'random generated title', pagesNumber: 'random generated pages number', }; const createBookSample = sinon.stub().returns(book); const generateBooks = generateBookSamplesCreator(createBookSample); const quantity = 25; const publisher = 'English house'; let bookSamples; beforeEach(() => { bookSamples = generateBooks({quantity, publisher}); createBookSample.resetHistory(); }); it('should create 25 books', () => { expect(bookSamples).to.have.lengthOf(quantity); }); it('should pass publisher name to the "book creator"', () => { expect(createBookSample.args).to.equal([{ publisher }]); }); });