Quality and Jest

Quality

Как можно повысить качество кода?

1 Code style and Linting

2 Type checking

Testing

JS Doc

TDD

Jest

Jest — это восхитительный фреймворк для тестирования JavaScript с акцентом на простоту

Jest

yarn add --dev jest

or

npm install --save-dev jest

Если нужны дополнительные настройки: 

- можно добавить в package.json или в специальном файле jest.config.js

Jest

Run:

"scripts": {
  "test": "jest",
}

 

npm run test

Jest

Jest состоит:

  • Suites
  • Specs
  • Expectation

Jest

describe('A suite', () => {
    test('contains spec with n expectation', () => {
        expect(Math.max(1, 5, 10)).toBe(10);
    });
})

Jest

describe('Match calculation', () => {
    test('should be true when max value present', () => {
        expect(Math.max(1, 5, 10)).toBe(10);
    });
})

Jest

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

describe('sum', () => {
    test('should be 3 when called sum with args 1 and 2', () => {
        expect(sum(1,2)).toBe(3);
    });
})

Unit test by TDD

const englishCode = "en-US";
const spanishCode = "es-ES";
function getAboutUsLink(language){
    switch (language.toLowerCase()){
        case englishCode.toLowerCase():
            return '/about-us';
        case spanishCode.toLowerCase():
            return '/acerca-de';
    }
    return '';
}

describe('getAboutUsLink', () => {
    test('should return correct route for en', () => {
        expect(getAboutUsLink(englishCode)).toBe('/about-us');
    });
})

Unit test by TDD

const englishCode = "en-US";
const spanishCode = "es-ES";
function getAboutUsLink(language){
    switch (language.toLowerCase()){
        case englishCode.toLowerCase():
            return '/about-us';
        case spanishCode.toLowerCase():
            return '/acerca-de';
    }
    return '';
}
module.exports = getAboutUsLink;

When code in different files

const getAboutUsLink = require("./app"); // file name

describe('getAboutUsLink', () => {
    test('should return correct route for en', () => {
        expect(getAboutUsLink('en-US')).toBe('/about-us');
    });
})

Unit test by TDD

const englishCode = "en-US";
const spanishCode = "es-ES";
function getAboutUsLink(language){
    switch (language.toLowerCase()){
        case englishCode.toLowerCase():
            return '/about-us';
        case spanishCode.toLowerCase():
            return '/acerca-de';
        default:
            return new Error('wrong route');
    }
}
module.exports = getAboutUsLink;

describe('getAboutUsLink', () => {
    test('should return an error', () => {
        try {
            getAboutUsLink('wrong');
        } catch (error) {
            expect(error.message).toBe('wrong route');
        }
    });
})

Testing failed flow

Different methods

function getUserInfo() {
    return {
        fistName: 'Vic',
        lastName: 'Vic2'
    }
}

describe('getUserInfo', () => {
    test('should return a correct user', () => {
        expect(getUserInfo()).toEqual({
            fistName: 'Vic',
            lastName: 'Vic2'
        });
    });
});

toEqual

Different methods

spyOn and toHaveBeenCalled

const myObj = {
    getUser(id) {
        return {
            firstName: 'Vic',
            lastName: 'Vic2'
        }
    }
};

describe('myObj', () => {
    test('should be called getUser', () => {
        const getUserSpy = jest.spyOn(myObj, 'getUser');
        myObj.getUser();
        expect(getUserSpy).toHaveBeenCalled();
    });

    test('should be called getUser with params', () => {
        const getUserSpy = jest.spyOn(myObj, 'getUser');
        myObj.getUser(1);
        expect(getUserSpy).toHaveBeenCalledWith(1);
    });
});

Complex task with babel

Add import supporting

  • npm install --save-dev @babel/plugin-transform-modules-commonjs
    
  • create .babelrc
{
  "env": {
    "test": {
      "plugins": ["@babel/plugin-transform-modules-commonjs"]
    }
  }
}

Mocking

import { UserStore } from './user'
export function getUserDisplayName(){
    const user = UserStore.getUser(1);
    return `${user.lastName}, ${user.firstName}`;
}
class User {
    getUser(userId){
        // logic to get data from a database
        return {
            firstName: 'Vic',
            lastName: 'Vic2'
        }
    }
    setUser(user){
        // logic to store data in a database
    }
}
let UserStore = new User();
export { UserStore }

Mocking

import { getUserDisplayName } from './app';

jest.mock('./user', () => ({
    UserStore: ({
        getUser: jest.fn().mockImplementation(arg => ({
            firstName: 'Ondrej',
            lastName: 'Polesny'
        })),
        setUser: jest.fn()
    })
}));


describe('getUserDisplayName', () => {
    test('should return user info', () => {
        expect(getUserDisplayName(1)).toBe("Polesny, Ondrej");
    });
});

Coverage

Для формирования информации о покрытии тестов используем команду jest --coverage.

"scripts": {
    "test": "jest",
    "test:coverage": "jest --coverage --collectCoverageFrom=src/**/*.{js,jsx}",
},

После запуска данной команды у нас будет репорт в консоле и также создастся отдельная папка с репортом coverage.

Coverage

jest

By Oleg Rovenskyi

jest

Quality and Jest

  • 177