Skip to content

Instantly share code, notes, and snippets.

@mrmartineau
Forked from tkrotoff/HowToTest.md
Created March 28, 2020 23:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrmartineau/84b2979d428b7581c65786f032f96a62 to your computer and use it in GitHub Desktop.
Save mrmartineau/84b2979d428b7581c65786f032f96a62 to your computer and use it in GitHub Desktop.
How I structure my tests

File structure

  • 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).

package.json

{
  "scripts": {
    "test": "NODE_ENV=test jest --verbose",
    "test:coverage": "jest --coverage",
    "test:e2e": "NODE_ENV=test jest --config jest-e2e.config.js"
  }
}

test()

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()', () => {
  ...
});

describe()

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

React components

// 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:

npm run test:coverage

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:

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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment