-
Notifications
You must be signed in to change notification settings - Fork 0
Testing
The library uses the jest testing framework for testing. Tests are located in the tests directory. The test configuration is located in the jest.config.js file. Tests are run using the jest command (or npm run test).
Test are written into files with the .test.ts extension. All files with this extension are automatically run by Jest.
Unit tests are located in the tests/unit directory. They test individual functions and classes. They are used to test the functionality of the library and to make sure that the library works as expected. They are also used to make sure that the library does not contain any bugs. They should cover main functions of the library.
Unit tests are usually structured in a similar way as the library itself. Multiple source files can have tests in a single test file. Some source files do not (even cannot) have tests. For example interface files do not have tests.
A unit test file test a feature. As this library is Object Oriented, a feature is usually a class, function or an interface. The name of the test file is a name of the suite of tests. The name of the suite should describe a common trait of all features tested in the suite.
The describe function is used to create a feature:
describe('feature', () => {
// tests
});Multiple features can be tested in a single test file.
The test function is used to create a test case:
describe('feature', () => {
test('test case', () => {
// code
// assertions
});
});Multiple test cases can be created for a single feature.
The expect function is used to create an assertion:
describe('feature', () => {
test('test case', () => {
expect(1).toBe(1);
});
});Multiple assertions can be created for a single test case. These assertions should however be related to each other as Jest stops the test case after the first failed assertion.
When a single feature has for example many branches or test cases, the test.each function shoud be used:
describe("Addition", () => {
const integerAdditions: {lhs: number, rhs: number, result: number}[] = {
{lhs: 1, rhs: 1, result: 2},
{lhs: 1, rhs: 2, result: 3},
{lhs: 2, rhs: 1, result: 3},
{lhs: 2, rhs: 2, result: 4},
};
const floatAdditions: {lhs: number, rhs: number, result: number}[] = {
{lhs: 1.1, rhs: 1.1, result: 2.2},
{lhs: 1.1, rhs: 2.2, result: 3.3},
{lhs: 2.2, rhs: 1.1, result: 3.3},
{lhs: 2.2, rhs: 2.2, result: 4.4},
};
test.each(integerAdditions)('addition of integers', ({lhs, rhs, result}) => {
expect(lhs + rhs).toBe(result);
});
test.each(floatAdditions)('addition of floats', ({lhs, rhs, result}) => {
expect(lhs + rhs).toBeCloseTo(result);
});
})End-to-end tests are located in the tests/e2e directory. They test the library as a whole. Such test is usually a basic use case of the library. They test that all parts of the library work together as expected.
All examples given in documentation and API documentation should be adapted into end-to-end tests. This ensures that the examples are correct and that they work as expected.
An end-to-end test file tests a use case. The name of the test file is a name of the use case. The describe function is used to create a use case:
describe('use case', () => {
// tests
});Usually a use case creates some objects, does some manipulations with them, and then checks the state of the objects. The test function should always test one aspect of the state. Code inside the test should have no side effects and be only related to the test it self. All manipulation should be done outside of the test block.
Standard expect assertions should be used to check the state of the objects:
describe('use case', () => {
// define objects
// manipulate objects
test('aspect of the state', () => {
// assertions
});
test('another aspect of the state', () => {
// assertions
});
// more objects manipulations
test('another aspect of the new state', () => {
// assertions
});
});If object manipulations can throw, they should also be placed inside the test block:
describe('use case', () => {
// define objects
test('object manipulation does not throw', () => {
// manipulate objects
});
test('aspect of the state', () => {
// assertions
});
});