# Jest: JavaScript unit test
###### tags: `Test`
> **Jest**: JavaScript Testing Framework
> **Enzyme**: JavaScript Testing utility for testing your React Components' output
> **React Testing Library(RTL)**: React DOM testing utilities
<br/>
## Test Overview







#### Test File Naming
Utils

Components

<br/>
---
## Unit Test Concept
### AAA -- Arrange, Act, Assert

`add.js`
```javascript=
export function add(numbers) {
let sum = 0;
for (const number of numbers) {
sum += +number;
}
return sum;
}
```
### Jest Syntax
**`it([descritiption string], [ test function])`**
**`test([descritiption string], [ test function])`**
```javascript=
it("description", ()=>{
// This unit test
// 1. Arrange
// 2. Act
// 3. Assert
})
```
`add.test.js`
```javascript=
import { add } from './add';
it("should sum up all number values in the array", () => {
// Arrange
const inputs = [1, 2, 3];
// Act
const result = add(inputs);
// Assert
const expectedResult = inputs.reduce((prevVal, curVal) => prevVal + curVal , 0);
expect(result).toBe(expectedResult);
});
```
<br/>
#### Keyword (method)
**`it`**/**`test`** : create a unit test
**`expect`** : assertion
[Jest Official Document: Expect Matcher](https://jestjs.io/docs/expect)
<br/>
>*What else do we want to test for this function?*
<br />
`add.test.js`
```javascript=
import { add } from './add';
it("should sum up all number values in the array", () => {
const inputs = [1, 2, 3];
const result = add(inputs);
const expectedResult = inputs.reduce((prevVal, curVal) => prevVal + curVal , 0);
expect(result).toBe(expectedResult);
});
it("should yield NaN if at least one invalid number is provided", () => {
const inputs = ["invalid", 1];
const result = add(inputs);
expect(result).toBeNaN();
});
it("should yield a correct sum if an array of numeric string values are provided", () => {
const inputs = ["1", "2"];
const result = add(inputs);
const expectedResult = inputs.map((numStr)=> +numStr).reduce((a,b) => a + b, 0);
expect(result).toBe(expectedResult);
});
it("should yield 0 if an empty array is provided", () => {
const inputs = [];
const result = add(inputs);
expect(result).toBe(0);
});
```
### Writing Good Test
- AAA
- Only Test **One** Thing
- Focus on the **essence** of a test when arranging
- Keep your number of assertioin(`expect`) **low**
<br />
## Other basic Jest method
### `describe`
> organize test suites
`validation.js`
```javascript=
export function validateStringNotEmpty(value) {
if (value.trim().length === 0) {
throw new Error('Invalid input - must not be empty.');
}
}
export function validateNumber(number) {
if (isNaN(number)) {
throw new Error('Invalid number input.');
}
}
```
<br/>
`validation.test.js`
```javascript=
import { it, expect, describe } from "vitest";
import { validateNumber, validateStringNotEmpty } from "./validation";
describe("validateStringNotEmpty()", () => {
it("should not throw an error error when non-empty string is provided", () => {
const input = " string ";
const resultFn = () => validateStringNotEmpty(input);
expect(resultFn).not.toThrow();
});
it("should throw an error when empty string is provided", () => {
const input1 = " ";
const input2 = "" ;
const resultFn1 = () => validateStringNotEmpty(input1);
const resultFn2 = () => validateStringNotEmpty(input2);
expect(resultFn1).toThrow("Invalid input - must not be empty.");
expect(resultFn2).toThrow("Invalid input - must not be empty.");
});
it('should throw an error if any other value than a string is provided', () => {
const inputNum = 1;
const inputBool = true;
const inputObj = {};
const validationFnNum = () => validateStringNotEmpty(inputNum);
const validationFnBool = () => validateStringNotEmpty(inputBool);
const validationFnObj = () => validateStringNotEmpty(inputObj);
expect(validationFnNum).toThrow();
expect(validationFnBool).toThrow();
expect(validationFnObj).toThrow();
});
})
describe("validateNumber()", () => {
it("should throw an error when input is NaN", () => {
const input = NaN;
const resultFn = () => validateNumber(input);
expect(resultFn).toThrow("Invalid number input.");
});
it("should not throw an error error when a number is provided", () => {
const input = 1;
const resultFn = () => validateNumber(input);
expect(resultFn).not.toThrow();
});
})
```
<br />
---
### Jest hooks :`beforeAll`, `beforeEach`, `afterAll`, `afterEach`
> Set up and clean-up among test in the same file
`user.js`
```javascript=
export class User {
constructor(email) {
this.email = email;
}
updateEmail(newEmail) {
this.email = newEmail;
}
clearEmail() {
this.email = '';
}
}
```
`user.test.js`
```javascript=
import { it, expect, beforeEach, beforeAll } from 'vitest';
import { User } from './user';
const testEmail = 'test@test.com';
let user;
beforeEach(() => {
user = new User(testEmail);
});
it('should update the email', () => {
const newTestEmail = 'test2@test.com';
user.updateEmail(newTestEmail);
expect(user.email).toBe(newTestEmail);
});
it('should have an email property', () => {
expect(user).toHaveProperty('email');
});
it('should store the provided email value', () => {
expect(user.email).toBe(testEmail);
});
it('should clear the email', () => {
user.clearEmail();
expect(user.email).toBe('');
});
it('should still have an email property after clearing the email', () => {
user.clearEmail();
expect(user).toHaveProperty('email');
});
```
---
### Concept: Mock and Spy
supplement
https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c
https://fork-risk-978.notion.site/Jest-mocks-081bccd7a8fe4e9dacb6b1ed02d1e898