---
# System prepended metadata

title: 'Jest: JavaScript unit test'
tags: [Test]

---

# 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

![](https://i.imgur.com/2DVHjvG.png)

![](https://i.imgur.com/R1eOWtb.jpg)

![](https://i.imgur.com/ZeZc4p9.png)

![](https://i.imgur.com/DFulCfV.jpg)

![](https://i.imgur.com/v1pjKOU.png)

![](https://i.imgur.com/KIDciU0.png)

![](https://i.imgur.com/jf3gOG7.png)

#### Test File Naming
Utils
![](https://i.imgur.com/iGQudLj.png)

Components
![](https://i.imgur.com/MU0VY81.png)


<br/>

---

## Unit Test Concept

### AAA -- Arrange, Act, Assert

![](https://i.imgur.com/ybDP62n.png)

`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








