- src/fooBar.js
- src/fooBar.html
- src/fooBar.scss
- src/fooBar....
- src/fooBar.test.js =>
npm run test
- src/fooBar.test.e2e.js (if I have E2E tests - Puppeteer, Playwright...) =>
npm run test:e2e
Tests should not be separated from the source code (think autonomous modules).
{
"scripts": {
"test": "NODE_ENV=test jest --verbose",
"test:coverage": "jest --coverage",
"test:e2e": "NODE_ENV=test jest --config jest-e2e.config.js"
}
}
As for the tests themselves, I follow the functions defined in the original source code (in the same order):
// fooBar.ts
export function fooBar1() {
...
}
export function fooBar2() {
...
}
// fooBar.test.ts
import { fooBar1, fooBar2 } from './fooBar';
test('fooBar1()', () => {
...
});
test('fooBar2()', () => {
...
});
If I have variations, I use describe() to re-group them:
// fooBar.test.ts
import { foorBar1, fooBar2 } from './fooBar';
describe('fooBar1()', () => {
test('case 1', () => {
...
});
test('case 2', () => {
...
});
});
test('fooBar2()', () => {
...
});
Example: https://github.com/tkrotoff/fetch/blob/v0.5.1/src/Http.test.ts#L88-L172
I try to avoid describe()
: most of the time they are unnecessary. Same for words like should, when, then...
=> just noise that brings no value
// MyComponent.test.tsx
import React from 'react';
import { MyComponent } from './MyComponent';
test('render', () => {
...
});
test('render without query param', () => {
...
});
test('render with query param', () => {
...
});
test('fetch error', () => {
...
});
(With React class components, I was writing test('render()', () => { ... })
since there was actually a render()
method; not anymore with hooks)
Examples:
- https://github.com/tkrotoff/MarvelHeroes/blob/8b8f4f93fb3d9a076830138ea6b821838e3fea46/src/Heroes.test.tsx
- https://github.com/tkrotoff/MarvelHeroes/blob/8b8f4f93fb3d9a076830138ea6b821838e3fea46/src/utils/ErrorBoundary.test.tsx
I ensure that fooBar.test.ts
fully covers fooBar.ts
by running npm run test:coverage fooBar.test.ts
.
If some stuffs are impossible to test, I use // istanbul ignore next
=> 100% code coverage :-)
Example: https://github.com/pmu-tech/stub-server/blob/v0.3.0/src/stubServer.ts#L84
Most of the time, fooBar.test.ts
is way bigger than fooBar.ts
, example:
- https://github.com/pmu-tech/stub-server/blob/v0.3.0/src/stubServer.ts => 164 LOC
- https://github.com/pmu-tech/stub-server/blob/v0.3.0/src/stubServer.test.ts => 403 LOC
This is because I test not only the nominal case but the corner cases too. My principle is "what ain't tested ain't working".
npm run test
performs jest --verbose
, this way I ensures my tests description are OK:
$ npm run test
PASS src/Http.test.ts
✓ throw TypeError (2ms)
defaults.init
✓ 201 Created + defaults (12ms)
✓ 201 Created + options + defaults (2ms)
getJSON()
✓ 500 Internal Server Error (2ms)
✓ 204 No Content (1ms)
200 OK
✓ 200 OK (2ms)
✓ 200 OK + options (2ms)
✓ 200 OK + options with method (3ms)
postJSON()
✓ 201 Created (2ms)
✓ 201 Created + options (2ms)
✓ 201 Created + options with method and body (2ms)
putJSON()
✓ 200 OK (1ms)
✓ 200 OK + options (1ms)
✓ 200 OK + options with method and body (3ms)
patchJSON()
✓ 200 OK (2ms)
✓ 200 OK + options (2ms)
✓ 200 OK + options with method and body (2ms)
deleteJSON()
✓ 204 No Content (1ms)
✓ 204 No Content + options (2ms)
✓ 204 No Content + options with method (2ms)
parseResponseBody()
✓ application/json Content-Type (1ms)
✓ text/plain Content-Type (1ms)
checkStatus()
✓ 200 OK (1ms)
✓ 400 Bad Request (5ms)
PASS src/HttpError.test.ts
✓ throw (2ms)
Test Suites: 2 passed, 2 total
Tests: 25 passed, 25 total
$ npm run test
PASS src/Layout.test.tsx
✓ render (47ms)
PASS src/PageNotFound.test.tsx
✓ render (56ms)
PASS src/utils/ErrorBoundary.test.tsx
✓ render children if no error (45ms)
if an error occured
✓ render message + report button (33ms)
✓ user clicks on report button (21ms)
withErrorBoundary()
✓ displayName (3ms)
✓ render children if no error (4ms)
✓ render a message if an error occured (8ms)
PASS src/Router.test.tsx
✓ HeroesPagination route (90ms)
✓ Hero route (10ms)
✓ PageNotFound route (5ms)
PASS src/Hero.test.tsx
✓ render (179ms)
✓ fetchCharacter() error (43ms)
PASS src/api/Marvel.test.ts
✓ getQueryParams() (4ms)
fetch*()
✓ fetchCharacters() success (4ms)
✓ fetchCharacters() error (6ms)
✓ fetchCharacter() success (1ms)
✓ fetchCharacter() error (2ms)
PASS src/utils/useErrorBoundary.test.tsx
✓ useErrorBoundary() (29ms)
PASS src/utils/fakeFetchResponse.test.ts
✓ fakeFetchResponseSuccess() (4ms)
✓ fakeFetchResponseError() (1ms)
PASS src/utils/getPackageNameFromPath.test.ts
✓ getPackageNameFromPath() (4ms)
PASS src/Heroes.test.tsx
✓ render (564ms)
✓ render "No results found :(" (6ms)
✓ fetchCharacters() error (21ms)
PASS src/HeroesPagination.test.tsx
✓ render without page query param then change page (581ms)
✓ render given a page query param (104ms)
Test Suites: 11 passed, 11 total
Tests: 27 passed, 27 total
$ npm run test
PASS bin/stub-server.test.ts
✓ correct config param (313ms)
✓ correct config and port params (309ms)
✓ network request (179ms)
✓ incorrect config param (146ms)
✓ incorrect port param (152ms)
PASS src/stubServer.test.ts
✓ delay (20ms)
✓ unknown route (2ms)
files
✓ file without HTTP status (48ms)
✓ json file does not exist
✓ png file does not exist (1ms)
✓ json (10ms)
✓ png (10ms)
✓ ts (8ms)
✓ js (6ms)
✓ html (5ms)
HTTP status codes
✓ invalid HTTP status code (6ms)
✓ 400 Bad Request (5ms)
✓ 500 Internal Server Error (5ms)
✓ 204 No Content (5ms)
HTTP verbs
✓ unknown HTTP verb (1ms)
✓ GET (4ms)
✓ compatibility with lower case (5ms)
✓ POST (8ms)
✓ PUT (5ms)
✓ PATCH (5ms)
✓ DELETE (5ms)
✓ multiple verbs (20ms)
proxy
mocked, no network request
✓ URL redirection with param (5ms)
✓ URL redirection to unknown host (12ms)
✓ POST multipart request (9ms)
not mocked, performs real network requests, might fail
✓ URL redirection with param (116ms)
✓ URL redirection to unknown host (24ms)
✓ POST multipart request (394ms)
express request handler
✓ js (6ms)
ts
✓ res.send() (6ms)
✓ res.send() async (506ms)
✓ res.status()
✓ res.end() (5ms)
✓ do nothing"
✓ without param (4ms)
get stub name from function
✓ no response property (5ms)
✓ with response property (5ms)
Test Suites: 2 passed, 2 total
Tests: 42 passed, 42 total