4. Some More Examples and Explanations
Tags: react, vitest, unittest, javascript
Published: 2024-06-20 18:50:25

# Example 1 :- Mocking Classes

```javascript
//returnNameOrAge.js
export const returnNameOrAge = (isName) => {
  if (isName) {
    return "My Name is XYZ"
  }

  return 25
}
```

```javascript
//someOtherFunction.js
class SomeModule {
  returnTrueOrFalse() {
    return false;
  }
}

export { SomeModule };
```

```javascript
//index.js
import { returnNameOrAge } from "./returnNameOrAge";
import { SomeModule } from "./someOtherFunctions";

const someModule = new SomeModule();

export const testVitest = () => {
  const dataFromSomeModule = someModule.returnTrueOrFalse();

  const dataFromReturnNameOrAge = returnNameOrAge(dataFromSomeModule);

  return dataFromReturnNameOrAge;
};
```

```javascript
//index.test.js
import { it, expect, describe, vi } from "vitest";
import { testVitest } from ".";
import { SomeModule } from "./someOtherFunctions";

vi.mock("./someOtherFunctions.js", async (importActual) => {
  const mod = await importActual();
  const SomeModule = vi.fn().mockReturnValue({
    returnTrueOrFalse: vi.fn(),
  });

  return {
    ...mod,
    SomeModule,
  };
});

describe("testVitest", () => {
  it("should return name", () => {
    const mockedSomeModule = new SomeModule();

    vi.mocked(mockedSomeModule.returnTrueOrFalse).mockReturnValue(true);
    
    const data = testVitest();

    expect(data).toBe("My Name is XYZ");
  });

  it("should return age", () => {
    const mockedSomeModule = new SomeModule();

    vi.mocked(mockedSomeModule.returnTrueOrFalse).mockReturnValue(false);

    const data = testVitest();

    expect(data).toBe(25);
  });
});
```

let's break down the modules and the test file

**Modules:**

1. **returnNameOrAge.js:** This module defines a simple function named `returnNameOrAge` that takes a boolean (`isName`) as input. If `isName` is true, it returns a string "My Name is XYZ". Otherwise, it returns the number 25.

2. **someOtherFunctions.js:** This module defines a class named `SomeModule`. The class has a method called `returnTrueOrFalse` that simply returns `false`. This class is likely intended to be mocked in the test.

3. **index.js:** This is the main module where everything comes together. It imports the `returnNameOrAge` function and the `SomeModule` class from the other modules. It then creates a new instance of `SomeModule` and defines a function called `testVitest`. The `testVitest` function calls the `returnTrueOrFalse` method of the `someModule` instance and then uses the return value to call the `returnNameOrAge` function. Finally, it returns the result from `returnNameOrAge`.

**Test File (index.test.js):**

This file uses the Vitest testing framework to test the `testVitest` function from `index.js`.

1. **Imports:** It imports the necessary functions from Vitest (`it`, `expect`, `describe`, and `vi`) for writing tests. It also imports the `testVitest` function from `index.js` and the `SomeModule` class from `someOtherFunctions.js`.

2. **Mocking:** It uses the `vi.mock` function to mock the `someOtherFunctions.js` module. This means that when the test runs, it won't use the actual implementation of the `SomeModule` class, but a mocked version instead. The mocked version is defined using a function that returns an object with a mocked `SomeModule` class. The mocked `SomeModule` class has a mocked `returnTrueOrFalse` method that can be controlled by the test.

3. **Test Cases:** The file defines two test cases using `describe` and `it`.
    - The first test case (`it("should return name")`) mocks the `returnTrueOrFalse` method of the mocked `SomeModule` to return `true`. It then calls the `testVitest` function and asserts that the returned value is "My Name is XYZ" using `expect`.
    - The second test case (`it("should return age")`) mocks the `returnTrueOrFalse` method of the mocked `SomeModule` to return `false`. It then calls the `testVitest` function and asserts that the returned value is 25 using `expect`.

**MAY I HAVE YOUR ATTENTION, PLEASE!!!**

Even though `index.js` depends on both `returnNameOrAge` and `SomeModule` only `SomeModule` but not `returnNameOrAge` is mocked in the test, here are the reason :-

- **Focus of the Test:** The test aims to isolate the logic of `testVitest` in `index.js`. This function relies on `returnNameOrAge` to format the output based on the value received from `SomeModule`. Since `returnNameOrAge` is a simple function with clear logic, mocking it might be unnecessary for this specific test.

- **Mocking Complexity:** Mocking a class like `SomeModule` allows for more control over its behavior. You can define what its methods return in different scenarios. In this case, the test wants to control the output of `returnTrueOrFalse` to verify how `testVitest` handles different inputs. Mocking `returnNameOrAge` wouldn't provide the same level of control.

- **Testing vs. Implementation:** Ideally, the functionality of `returnNameOrAge` should be tested in its own dedicated test file. This keeps the tests focused and avoids redundancy. Mocking it in this test would be testing an implementation detail rather than the overall logic of `testVitest`.



# Example 2 :- Mocking Functions

```javascript
//returnNameOrAge.js
export const returnNameOrAge = (isName) => {
  if (isName) {
    return "My Name is XYZ"
  }

  return 25
}
```

```javascript
//someOtherFunction.js
export const someOtherFunction = () => {
  const testValue = true;

  return testValue;
};
```

```javascript
//index.js
import { returnNameOrAge } from "./returnNameOrAge";
import { someOtherFunction } from "./someOtherFunctions";

export const testVitest = () => {
  const dataFromSomeOtherFunction = someOtherFunction();

  const dataFromReturnNameOrAge = returnNameOrAge(dataFromSomeOtherFunction);

  return dataFromReturnNameOrAge;
};
```

```javascript
//index.test.js
import { it, expect, describe, vi } from "vitest";
import { testVitest } from ".";

const mocks = vi.hoisted(() => ({
  someOtherFunction: vi.fn(),
}));

vi.mock("./someOtherFunctions.js", () => ({
  someOtherFunction: mocks.someOtherFunction,
}));

describe("testVitest", () => {
  it("should return name", () => {
    mocks.someOtherFunction.mockReturnValue(true);
    const data = testVitest();

    expect(data).toBe("My Name is XYZ");
  });

  it("should return age", () => {
    mocks.someOtherFunction.mockReturnValue(false);

    const data = testVitest();

    expect(data).toBe(25);
  });
});
```

let's break down the modules and the test file

**Modules:**
1. **returnNameOrAge.js:**
    - This module defines a function named `returnNameOrAge` that takes a boolean (`isName`) as input.
    - If `isName` is true, it returns the string "My Name is XYZ".
    - Otherwise, it returns the number 25.
2. **someOtherFunctions.js:**
    - This module defines a function named `someOtherFunction`.
    - This function simply creates a constant `testValue` with the value `true` and returns it.
3. **index.js:**
    - This is the main module.
    - It imports `returnNameOrAge` from `returnNameOrAge.js` and `someOtherFunction` from `someOtherFunctions.js`.
    - It defines a function called `testVitest`.
    - `testVitest` calls `someOtherFunction` to get a value.
    - It then calls `returnNameOrAge` with the value from `someOtherFunction` and returns the result.

**Test File (index.test.js):**

This file uses the Vitest testing framework to test the `testVitest` function from `index.js`.
1. **Imports:**
    - It imports necessary functions from Vitest for writing tests (`it`, `expect`, `describe`, and `vi`).
    - It imports the `testVitest` function from `index.js`.
2. **Mocking:**
    - It uses the `vi.mock` and `vi.hoisted` functions together to mock the `someOtherFunction`. Here's a breakdown:
        - `vi.hoisted` creates a function that returns an object to hold mocks. This ensures mocks are created only once before each test.
        - Inside `vi.mock`, it replaces the `someOtherFunction` from `someOtherFunctions.js` with the mocked version from the `mocks` object.

3. **Test Cases:**
    - The file defines two test cases using `describe` and `it`.
        - The first test (`"should return name"`) mocks `someOtherFunction` to return `true` (using `mockReturnValue`). It then calls `testVitest` and asserts the returned value is "My Name is XYZ" using `expect`.
        - The second test (`"should return age"`) mocks `someOtherFunction` to return `false`. It then calls `testVitest` and asserts the returned value is 25 using `expect`.

# Example 3 :- Actual Import Mocking and/or Hoisting+Mocking

Now, We'll write two unit tests for the same function, each leveraging a different mocking approach. The first test case will demonstrate mocking the actual import, while the second will explore the concept of hoisting combined with mocking. By comparing these techniques, you'll gain valuable insights into their strengths and how to choose the most suitable method for your specific testing needs.

```javascript
// userService.js
import axios from "axios";

const userService = async () => {
  const { data } = await axios.get(
    "https://jsonplaceholder.typicode.com/users/1",
  );

  return data;
};

export default userService;
```

```javascript
// approach involving hoisting and mocking (this approach doesn't involve importing axios in the test file)
import userService from "./userService";

const mockedAxios = vi.hoisted(() => ({
  default: {
    get: vi.fn(),
  },
}));

vi.mock("axios", () => ({
  default: mockedAxios.default,
}));

test("testing", async () => {
  mockedAxios.default.get.mockResolvedValue({
    data: {
      id: 1,
    },
  });

  const data = await userService();

  expect(data.id).toBe(1);
});
```

```javascript
// approach involving mocking using importActual (this approach involve importing axios in the test file)
import axios from "axios";
import userService from "./userService";

vi.mock("axios", async () => {
  const axios = await vi.importActual("axios");
  const get = vi.fn();

  return {
    default: {
      ...axios,
      get,
    },
  };
});

test("testing", async () => {
  vi.mocked(axios.get).mockResolvedValue({
    data: {
      id: 1,
    },
  });
  const data = await userService();

  expect(data.id).toBe(1);
});
```

`importActual` can also be used as following as well

```javascript
vi.mock("axios", async (importActual) => {
  const axios = await importActual<typeof import("axios")>();
  const get = vi.fn();

  return {
    default: {
      ...axios,
      get,
    },
  };
});
```

**KEY TAKE AWAYS**

1. general use cases of`importActual` :- class mocking, function mocking especially when these functions are spied on {*`vi.spyOn()`*} (good use cases example for `spyOn` would be to unit test how many times the function has been executed)

2. use case of `hoisting+mocking` :- for general test cases