JavaScript Testing 
For more details checkout Testing
Unit Testing Libraries 
Test runners:
Mocha 
Mocha is a test framework for Node.js programs, featuring:
- Browser support
- Asynchronous testing
- Test coverage reports
- Use of any assertion library
- Really good for Node.js projects
- No Test double library included (use Sinon)
- No assertions library included (use Chai)
Example:
var expect = require("chai").expect;
// test suite
describe("Mocha", function () {
  // test spec (unit test)
  it("should run our tests using Mocha", function () {
    expect(true).to.be.ok;
  });
});TODO: Check mocha's execution context being reset after each test case execution?
Chai 
Chai is an expectation or assertion library
- expectis used to run BDD style test casesjavascript- var expect = require("chai").expect; expect(true).to.be.true;
- The above code will not provide any output if the expectation is met 
Jest 
Jest is a JavaScript unit testing framework, used by Facebook to test services and React applications
- It is a test runner that lets us access the DOM via js-dom - js-dom is a JavaScript implementation of various web standards, for use with Node.js 
- js-dom is only an approximation of how the browser works, it is often good enough for testing React components 
 
- Test runner that: - Finds tests
- Runs tests
- Determines whether tests pass or fail
 
- It has an assertion library and mocking library 
- Jest looks for filenames ending with - .test.js
Setup:
- Install it using any package manager: bash- npm install --save-dev jest
- Generate a basic configuration file: bash- jest --init
- Write test cases 
- Run test cases 
Example:
// sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
// sum.test.js
const sum = require("./sum");
test("adds 1 + 2 to equal 3", () => {
  expect(sum(1, 2)).toBe(3);
});Tests 
- Global testoritmethod has 2 required and 1 optional argument: creates a test closure- name: The name of your test
- fn: The function for your test
- timeout: The timeout for an async function test
 
test("adds 1 + 2 to equal 3", () => {
  expect(sum(1, 2)).toBe(3);
});
// or
it("adds 1 + 2 to equal 3", () => {
  expect(sum(1, 2)).toBe(3);
});- Test fails if error is thrown when running function - Assertions throw errors when expectation fails
 
- No error --> tests pass - Empty test passes!
 
- Run only this test: javascript- test.only("adds 1 + 2 to equal 3", () => { expect(sum(1, 2)).toBe(3); }); fit("adds 1 + 2 to equal 3", () => { expect(sum(1, 2)).toBe(3); });
- Skip this test: javascript- test.skip("adds 1 + 2 to equal 3", () => { expect(sum(1, 2)).toBe(3); }); xit("adds 1 + 2 to equal 3", () => { expect(sum(1, 2)).toBe(3); });
Suite 
Suite is a specific group of related tests
- In jest each file is test suite
Tests can be grouped together using a describe block
- Each test file can have any number of describeblocks
// describe(name: 'group name', fn: 'function that contains tests')
describe("matching cities to foods", () => {
  test("Vienna <3 veal", () => {
    expect(isValidCityFoodPair("Vienna", "Wiener Schnitzel")).toBe(true);
  });
  test("San Juan <3 plantains", () => {
    expect(isValidCityFoodPair("San Juan", "Mofongo")).toBe(true);
  });
});Matchers 
"Matchers" let you test values in different ways
- expect()returns an "expectation" object, we can call matchers on them
Commonly used matchers:
- Truthiness: - toBeNullmatches only- null
- toBeUndefinedmatches only- undefined
- toBeDefinedis the opposite of- toBeUndefined
- toBeTruthymatches anything that an- ifstatement treats as- true
- toBeFalsymatches anything that an- ifstatement treats as- false
 
- Numbers: - toBeGreaterThan()
- toBeGreaterThanOrEqual()
- toBeLessThan()
- toBeLessThanOrEqual()
- toBeand- toEqualare equivalent for numbers
- For floating point equality, use toBeCloseToinstead oftoEqual, because you don't want a test to depend on a tiny rounding error
 
- Strings: - toMatch: check strings against regular expressions
 javascript- test("there is no I in team", () => { expect("team").not.toMatch(/I/); }); test('but there is a "stop" in Christoph', () => { expect("Christoph").toMatch(/stop/); });
- Arrays and iterables: - toContain: array or iterable contains a particular item
 
- Exceptions: - toThrow: test whether a particular function throws an error when it's called
 javascript- function compileAndroidCode() { throw new Error("you are using the wrong JDK"); } test("compiling android goes as expected", () => { expect(() => compileAndroidCode()).toThrow(); expect(() => compileAndroidCode()).toThrow(Error); // You can also use the exact error message or a regexp expect(() => compileAndroidCode()).toThrow("you are using the wrong JDK"); expect(() => compileAndroidCode()).toThrow(/JDK/); });
Mocking 
const add = jest.fn(() => 3);
test("adds 1 + 2 to equal 3", () => {
  expect(add(1, 2)).toBe(3);
  expect(add(9, 6)).toBe(3);
});
// test passed
test("adds 1 + 2 to equal 3", () => {
  expect(add(1, 2)).toBe(3);
  expect(add).toHaveBeenCalledTimes(1);
  expect(add).toHaveBeenCalledWith(1, 2);
});- Manually mocking - fetch:javascript- // src/utils/currency.test.js global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ rates: { CAD: 1.42 } }), }), ); beforeEach(() => { fetch.mockClear(); }); it("finds exchange", async () => { const rate = await convert("USD", "CAD"); expect(rate).toEqual(1.42); expect(fetch).toHaveBeenCalledTimes(1); }); // Mocks with failure it("returns null when exception", async () => { fetch.mockImplementationOnce(() => Promise.reject("API is down")); const rate = await convert("USD", "CAD"); expect(rate).toEqual(null); expect(fetch).toHaveBeenCalledWith( "https://api.exchangeratesapi.io/latest?base=USD", ); });
- Faking API calls made using - fetch:javascript- // src/utils/currency.js async function convert(base, destination) { try { const result = await fetch( `https://api.exchangeratesapi.io/latest?base=${base}`, ); const data = await result.json(); return data.rates[destination]; } catch (e) { return null; } } export { convert };
