Unit tests are mainly to verify that the code works as expected, so common statements for single tests are expect(XXX).toxxx ().

Use case example

const sum = require('./sum');
test('adds 1 + 2 to equal 3'.() = > {
  expect(sum(1.2)).toBe(3);
});
Copy the code

Cases of

  1. Reference the function under test
  2. Use case descriptiontestit
  3. Calling the specified method
  4. Determine whether the results meet expectations

matcher

Judgment is equal

 .toBe(2)   / / values are equal
 .toEqual({one: 1.two: 2}); // object
 .toBeNull()  // null
 .toBeUndefined()  // undefiend
 .toBeDefined()  // The opposite is true
 .toBeTruthy()   // true
 .toBeFalsy()    // false
Copy the code

not

test('zero'.() = > {
  const z = 0;
  expect(z).not.toBeNull();
  expect(z).not.toBeNaN();
  expect(z).toBeDefined();
  expect(z).not.toBeUndefined();
  expect(z).not.toBeTruthy();
  expect(z).toBeFalsy();
});

Copy the code

digital

test('two plus two'.() = > {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(3.5);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4.5);

  // toBe and toEqual are equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
  
   const value = 0.1 + 0.2;
  / / expect (value). Place (0.3); This is an error because floating point numbers have rounding errors
  expect(value).toBeCloseTo(0.3); // This sentence can run
});
Copy the code

Regular match

test('there is no I in team'.() = > {
  expect('team').not.toMatch(/I/);
});
Copy the code

contains

const shoppingList = [
  'diapers'.'kleenex'.'trash bags'.'paper towels'.'milk',]; test('the shopping list has milk on it'.() = > {
  // String, array
  expect(shoppingList[0]).toContain('a');
  expect(shoppingList).toContain('milk');
  expect(new Set(shoppingList)).toContain('milk');
})

Copy the code

toThrow

function compileAndroidCode() {
  throw new Error('you are using the wrong JDK');
}

test('compiling android goes as expected'.() = > {
  expect(() = > compileAndroidCode()).toThrow();
})
Copy the code

promise

test('resolves to lemon'.() = > {
  // make sure to add a return statement
  expect(Promise.resolve('lemon')).resolves.toBe('lemon');
  expect(Promise.reject('apple')).rejects.toBe('apple');
});
Copy the code

instance

class A {}

expect(new A()).toBeInstanceOf(A);
expect(() = > {}).toBeInstanceOf(Function);
Copy the code

Common processing cases

asynchronous

Normally the test code is executed synchronously, but when the code we are testing is asynchronous, there is a problem, causing the test case to end, but our asynchronous code is not executed, and thus the asynchronous code is not tested. We can call the specified method to tell Jest that our asynchronous execution is complete

done

If the test function passes done, jest will wait until done is called before terminating the current test case. If done is not called, the test will automatically fail.

it('Test async code with done'.(done) = > {
  setTimeout(() = > {
    // expect something
    done();
  }, 1000)});Copy the code

promise

If a Promise is used in your code, you can handle asynchronous code by returning a Promise. Then and catch are used to judge the return using Jest’s convergent and rejects methods

And then catch

// Assuming doAsync() returns a promise, resolve results in the string 'example'
it('Test async code with promise'.() = > {
  expect.assertions(1);
  return doAsync().then((data) = > {
    expect(data).toBe('example');
  });
});

it('Test promise with an error'.() = > {
  expect.assertions(1);
  return doAsync().catch(e= > expect(e).toMatch('error'));
});

Copy the code

Resolves and rejects

// Assuming doAsync() returns a promise, resolve results in the string 'example'
it('Test async code with promise'.() = > {
  expect.assertions(1);
  return expect(doAsync()).resolves.toBe('example');
  });
});

it('Test promise with an error'.() = > {
  expect.assertions(1);
  return expect(doAsync()).rejects.toMatch('error'));
});
Copy the code

Expect. Assertions (n) Is used to ensure the number of expect executions

async/await

Async /await uses promise syntactic sugar

// Assuming doAsync() returns a promise, resolve results in the string 'example'
it('Test async code with promise'.async () => {
  expect.assertions(1);
  const data = await doAsync();
  expect(data).toBe('example');
  });
});
Copy the code

Async /await can also be used together with /rejects

// Assuming doAsync() returns a promise, resolve results in the string 'example'
it('Test async code with promise'.async () => {
  expect.assertions(1);
  await expect(doAsync()).resolves.toBe('example');
  });
});
Copy the code

mock

In a project, it is common for methods in one module to call methods in another module. In unit tests, we might not care about the execution and results of an internally called method, just whether it was called correctly, and even specify the return value of that function. In this case, it is necessary to use Mock functions.

Mock functions provide three features that are useful when writing test code:

  • Capture function calls
  • Sets the return value of the function
  • Change the internal implementation of a function

jest.fn()

Jest.fn () is the easiest way to create Mock functions, and if the internal implementation of the function is not defined, jest.fn() returns undefined as the return value.

We can tell what f sub n is

  • Call return value
  • Whether a callback is called (often used to determine whether a callback is called)
  • Call the number
  • Arguments received when called
test('Test the jest. Fn () call'.() = > {
  let mockFn = jest.fn();
  let result = mockFn(1.2.3);

  // Assert that execution of mockFn returns undefined
  expect(result).toBeUndefined();
  // Assert mockFn is called
  expect(mockFn).toBeCalled();
  // Assert that mockFn is called once
  expect(mockFn).toBeCalledTimes(1);
  // Assert that mockFn passes in arguments 1, 2, 3
  expect(mockFn).toHaveBeenCalledWith(1.2.3);
})
Copy the code

Jest. Fn () can also

  • Set the return value
  • Define the internal implementation
  • returnPromiseObject.
test('Test jest.fn() returns fixed value'.() = > {
  let mockFn = jest.fn().mockReturnValue('default');
  Assert that mockFn returns a value of default after execution
  expect(mockFn()).toBe('default');
})

test('Test the internal implementation of jest.fn()'.() = > {
  let mockFn = jest.fn((num1, num2) = > {
    return num1 * num2;
  })
  MockFn returns 100 after execution
  expect(mockFn(10.10)).toBe(100);
})

test('Test jest.fn() returns Promise'.async() = > {let mockFn = jest.fn().mockResolvedValue('default');
  let result = await mockFn();
  // assert mockFn returns default after execution with await keyword
  expect(result).toBe('default');
  // Assert that a mockFn call returns a Promise object
  expect(Object.prototype.toString.call(mockFn())).toBe("[object Promise]");
})
Copy the code

jest.mock()

When we do unit tests, the files under test may reference other modules. But we usually only want to test the execution of a single file, in which case we can mock the entire file

So if we want to test the sum file, and sum calls utils, then maybe there’s something going on in init that we didn’t provide when we tested it in a single file, and sometimes we get an error so we don’t really care what the logic of init is, we just need to call it, okay

const init = require('./utils')
function sum(a, b) {
    init();
    return a+b;
}
module.exports = sum;

Copy the code

At this point our test case could simply mock the entire module and verify that it was called

const init = require('./utils')
const sum = require('./sum');

jest.mock('./utils.js')
it('adds 1 + 2 to equal 3'.() = > {
  expect(sum(1.2)).toBe(3);
  expect(init).toHaveBeenCalled();
});
Copy the code

Sometimes my code might look like this, and I’ll call a function and I’ll use a return value and then we can mock out that return

Let’s say this init returns a Promise

function init(num) {
   // Perform a series of complex operations
   return Promise.resolve(num)
}
module.exports = init;
Copy the code

And the sum method uses this return value

const init = require('./utils')
function sum(a, b) {
    return init(a + b) 
}
module.exports = sum;
Copy the code

At this point you can mock to specify what to return

const init = require('./utils')
const sum = require('./sum');

jest.mock('./utils.js'.() = >{

    return () = > {
      return Promise.resolve(Awesome!);
    }
})
it('adds 1 + 2 to equal 3'.() = > {
  const result = init(sum(1.2))
  result.then(r= >{
    expect(r).toBe(Awesome!); })});Copy the code

jest.spyOn()

SpyOn can be used when we mock and want the code inside the mock function to be executed and we want to verify that the function was called

const video = {
  play() {
    return true; }};module.exports = video;
Copy the code
const video = require('./video');

test('plays video'.() = > {
  const spy = jest.spyOn(video, 'play');
  const isPlaying = video.play();

  expect(spy).toHaveBeenCalled();
  expect(isPlaying).toBe(true);
});
Copy the code

SpyOn creates a mock function that does the same thing as the original function so that the function remains intact and you can use the mock function’s methods to verify the call

Before/after series

beforeAll

Run a function before any tests in this file run.

For example, connect to the database first when testing data

const DB = require('./db.js');

beforeAll(() = > {
  // Connect data before starting the test
  // Jest will wait for the Promise to return resolve to run the test.
  return DB.clear().connect()
});

// After initializing the data, perform the operation
test('can find things'.() = > {
  return DB.find('thing', {}, results= > {
    expect(results.length).toBeGreaterThan(0);
  });
});

// Omit a bunch of data tests
Copy the code

afterAll

Run a function after all the tests in this file are complete.

For example, after testing, close the connection

const DB = require('./db.js');

beforeAll(() = > {
  // Connect data before starting the test
  // Jest will wait for the Promise to return resolve to run the test.
  return DB.connect()
});

// After initializing the data, perform the operation
test('can find things'.() = > {
  return DB.find('thing', {}, results= > {
    expect(results.length).toBeGreaterThan(0);
  });
});

// Omit a bunch of data tests

afterAll(() = > {
  // Close the connection after all tests have finished
  // Jest will wait for the Promise to return resolve to run the test.
  return DB.close()
});
Copy the code

beforeEach

Run a function before each test run.

Like trying to get a new instance every time you test

const Event = require('./event')
let instance

beforeEach(() = > {
  instance  = new Event()
});

test('on'.() = > {
  let fn = jest.fn()
  instance.on('click',fn)
  instance.emit('click')
  expect(fn).toHaveBeenCalled()
});
Copy the code