By Stefano Magni and Jaga Santagostino
Senior front-end developer
Fullstack JavaScript developer
All the contents and the code of the course are available at
A test is code that throws an error when the actual result of something does not match the expected output.
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
let result, expected;
result = sum(3, 7);
expected = 10;
if (result !== expected) {
throw new Error(`${result} is not equal to ${expected}`);
}
result = subtract(7, 3);
expected = 4;
if (result !== expected) {
throw new Error(`${result} is not equal to ${expected}`);
}
The previous "tests" rewritten for Jest (and the Jest output,when launched in the terminal).
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
test("Should sum", () => {
expect(sum(3, 7)).toEqual(10);
});
test("Should subtract", () => {
expect(subtract(7, 3)).toEqual(4);
});
PASS tests/test-introdution/base.test.js
✓ Should sum (1ms)
✓ Should subtract
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.08s, estimated 1s
They allow organizing tests.
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
describe("Math operations", () => {
test("Should sum", () => {
expect(sum(3, 7)).toEqual(10);
});
test("Should subtract", () => {
expect(subtract(7, 3)).toEqual(4);
});
});
PASS tests/test-introdution/describe.test.js
Math operations
✓ Should sum (1ms)
✓ Should subtract
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.072s, estimated 1s
They allow isolating the tests.
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
describe("Math operations", () => {
test.only("Should sum", () => {
expect(sum(3, 7)).toEqual(10);
});
test("Should subtract", () => {
expect(subtract(7, 3)).toEqual(4);
});
});
PASS tests/test-introdution/only.test.js
Math operations
✓ Should sum (2ms)
○ skipped Should subtract
Test Suites: 1 passed, 1 total
Tests: 1 skipped, 1 passed, 2 total
Snapshots: 0 total
Time: 0.066s, estimated 1s
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
describe("Math operations", () => {
test.skip("Should sum", () => {
expect(sum(3, 7)).toEqual(10);
});
test("Should subtract", () => {
expect(subtract(7, 3)).toEqual(4);
});
});
PASS tests/test-introdution/skip.test.js
Math operations
✓ Should subtract (1ms)
○ skipped Should sum
Test Suites: 1 passed, 1 total
Tests: 1 skipped, 1 passed, 2 total
Snapshots: 0 total
Time: 0.066s, estimated 1s
They allow running common code before/after the tests.
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
describe("Math operations", () => {
let i = 0;
afterEach(() => {
i++;
console.log(`${i} tests run`);
});
test("Should sum", () => {
expect(sum(3, 7)).toEqual(10);
});
test("Should subtract", () => {
expect(subtract(7, 3)).toEqual(4);
});
});
PASS tests/test-introdution/after-each.test.js
Math operations
✓ Should sum (1ms)
✓ Should subtract (1ms)
console.log tests/test-introdution/after-each.test.js:8
1 tests run
console.log tests/test-introdution/after-each.test.js:8
2 tests run
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.311s, estimated 1s
to control that the code does what we expect
to avoid direct and indirect regressions
to reproduce corner cases
to leverage the speed of an automated tool
to leverage forever the automatic checks
to tell a story about the code
to prevent problems instead of facing them
to kill refactoring fear
to get us working with a lower cognitive load
• we must write the test as soon as we write the code
• false-negative tests are evil
• the tests must be simple to be read
• avoid DRYing the test code too early
• avoid conditional tests
• care about the test feedback in case of failures
• the tests must be deterministic and independent
• sleep(1000) is evil too
• the test must fail before you write the code and succeed when you have written the code
• the tests must be fast, as fast as possible
• never do white-box testing
• code coverage helps us find what we have not tested yet, it is not an end in itself
• if you're not enough confident about your application, think twice about your tests
• you should not test external services, third-party libraries, and native APIs
• last but not least: remember that testing and TDD are two different things
• tests allow you to study
• testing throw responsibility away (in good meaning) because you are no more an elephant in a glass shop. Instead, while touching some code, you are part of a protected-by-tests sandbox, coding becomes easier
Unit testing:
> function() {}
Integration testing:
> function() {
function() {}
function() {}
function() {}
}
React unit/component testing:
>
React Integration testing:
>
With Jest
With Jest + React Testing Library
UI Integration testing:
With Cypress (Chrome) + Cypress Testing Library
E2E testing:
Unit tests
Integration tests
UI tests
Unit tests
Integration tests
UI tests