๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
์™ธ๋ถ€ API, ํŒŒ์ผ ์‹œ์Šคํ…œ, ํƒ€์ด๋จธ ๋“ฑ์˜ ์˜์กด์„ฑ์„ ๋ชจํ‚นํ•˜์—ฌ ๋…๋ฆฝ์ ์ธ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

API Mocking์ด๋ž€?

ํ…Œ์ŠคํŠธ์—์„œ ์™ธ๋ถ€ ์˜์กด์„ฑ(API, ํŒŒ์ผ ์‹œ์Šคํ…œ, ์‹œ๊ฐ„ ๋“ฑ)์„ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ๊ฐ€์งœ ๋™์ž‘์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ Mocking์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด:
  • ์™ธ๋ถ€ ์„œ๋น„์Šค ์žฅ์• ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ์‹คํ–‰
  • ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ ์—†์ด ๋น ๋ฅธ ํ…Œ์ŠคํŠธ
  • ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ์•ˆ์ •์ ์ธ ํ…Œ์ŠคํŠธ
  • ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜๋Š” ์ž‘์—…(๊ฒฐ์ œ, SMS ๋“ฑ) ํšŒํ”ผ

Mocking์˜ ์ข…๋ฅ˜

Sonamu์—์„œ๋Š” ์„ธ ๊ฐ€์ง€ ๋ฐฉ์‹์˜ Mocking์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค:

Vitest Mock

Vitest์˜ ๊ธฐ๋ณธ Mocking ๊ธฐ๋Šฅ

Naite Mock

Naite๋ฅผ ํ™œ์šฉํ•œ ๋™์  Mocking

Manual Mock

์ˆ˜๋™์œผ๋กœ ๊ตฌํ˜„ํ•œ Mock ๊ฐ์ฒด

Vitest Mock ์‚ฌ์šฉํ•˜๊ธฐ

๋ชจ๋“ˆ Mocking

์™ธ๋ถ€ ๋ชจ๋“ˆ์˜ ๋™์ž‘์„ ๊ฐ€์งœ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค:
import { test, vi } from "vitest";
import axios from "axios";

// axios ๋ชจ๋“ˆ ์ „์ฒด๋ฅผ Mock์œผ๋กœ ๋Œ€์ฒด
vi.mock("axios");

test("์™ธ๋ถ€ API ํ˜ธ์ถœ", async () => {
  // Mock ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’ ์„ค์ •
  vi.mocked(axios.get).mockResolvedValue({
    data: { id: 1, name: "John" },
  });
  
  const result = await userService.fetchUser(1);
  
  expect(result.name).toBe("John");
  expect(axios.get).toHaveBeenCalledWith("/api/users/1");
});

ํ•จ์ˆ˜ Spy

์‹ค์ œ ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค:
import { test, vi } from "vitest";
import { emailService } from "../services/email.service";

test("์ด๋ฉ”์ผ ๋ฐœ์†ก ํ™•์ธ", async () => {
  // ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์ถ”์ ํ•˜๋Š” Spy ์ƒ์„ฑ
  const sendSpy = vi.spyOn(emailService, "send").mockResolvedValue(true);
  
  await userService.register({ email: "[email protected]" });
  
  // ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
  expect(sendSpy).toHaveBeenCalledWith({
    to: "[email protected]",
    subject: "ํšŒ์›๊ฐ€์ž…์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค",
  });
  
  // Spy ์ •๋ฆฌ
  sendSpy.mockRestore();
});

ํƒ€์ด๋จธ Mocking

์‹œ๊ฐ„ ๊ด€๋ จ ํ•จ์ˆ˜๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค:
import { test, vi } from "vitest";

test("setTimeout ํ…Œ์ŠคํŠธ", async () => {
  // ๊ฐ€์งœ ํƒ€์ด๋จธ ์‚ฌ์šฉ
  vi.useFakeTimers();
  
  let executed = false;
  setTimeout(() => {
    executed = true;
  }, 1000);
  
  // 1์ดˆ ์ „
  expect(executed).toBe(false);
  
  // ์‹œ๊ฐ„ 1์ดˆ ์ง„ํ–‰
  vi.advanceTimersByTime(1000);
  
  // 1์ดˆ ํ›„
  expect(executed).toBe(true);
  
  // ์‹ค์ œ ํƒ€์ด๋จธ๋กœ ๋ณต์›
  vi.useRealTimers();
});
bootstrap()๊ณผ ํƒ€์ด๋จธ: Sonamu์˜ bootstrap() ํ•จ์ˆ˜๋Š” afterEach์—์„œ ์ž๋™์œผ๋กœ vi.useRealTimers()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํƒ€์ด๋จธ๋ฅผ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„๋กœ ํ˜ธ์ถœํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋‚ ์งœ Mocking

ํ˜„์žฌ ์‹œ๊ฐ์„ ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค:
import { test, vi } from "vitest";

test("์˜ค๋Š˜ ๋‚ ์งœ ํ™•์ธ", () => {
  // 2025๋…„ 1์›” 1์ผ๋กœ ๊ณ ์ •
  vi.setSystemTime(new Date("2025-01-01T00:00:00Z"));
  
  const today = new Date();
  expect(today.getFullYear()).toBe(2025);
  expect(today.getMonth()).toBe(0);  // 0 = January
  
  // ์‹œ์Šคํ…œ ์‹œ๊ฐ„ ๋ณต์›
  vi.useRealTimers();
});

Naite Mock ์‚ฌ์šฉํ•˜๊ธฐ

Naite์˜ ํŠน๋ณ„ํ•œ ํ‚ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ ์œผ๋กœ Mock์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ผ ์‹œ์Šคํ…œ Mocking

๊ฐ€์ƒ ํŒŒ์ผ ์ƒ์„ฑ

mock:fs/promises:virtualFileSystem ํ‚ค๋กœ ๊ฐ€์ƒ ํŒŒ์ผ์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค:
import { test } from "sonamu/test";
import { Naite } from "sonamu";
import { access, constants } from "fs/promises";
import { expect } from "vitest";

test("๊ฐ€์ƒ ํŒŒ์ผ ์‹œ์Šคํ…œ", async () => {
  const filePath = "/tmp/my-virtual-file.txt";
  
  // 1. ์‹ค์ œ๋กœ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ
  await expect(
    access(filePath, constants.F_OK)
  ).rejects.toThrow();
  
  // 2. Naite๋กœ ๊ฐ€์ƒ ํŒŒ์ผ ๋“ฑ๋ก
  Naite.t("mock:fs/promises:virtualFileSystem", filePath);
  
  // 3. ์ด์ œ ํŒŒ์ผ์ด "์กด์žฌ"ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ž‘
  await expect(
    access(filePath, constants.F_OK)
  ).resolves.toBeUndefined();
  
  // 4. Mock ์‚ญ์ œ
  Naite.del("mock:fs/promises:virtualFileSystem");
  
  // 5. ๋‹ค์‹œ ์กด์žฌํ•˜์ง€ ์•Š์Œ
  await expect(
    access(filePath, constants.F_OK)
  ).rejects.toThrow();
});

์‹ค์ „ ์˜ˆ์ œ: ํŒŒ์ผ ์—…๋กœ๋“œ ํ…Œ์ŠคํŠธ

import { test } from "sonamu/test";
import { Naite } from "sonamu";
import { fileService } from "../services/file.service";
import { expect } from "vitest";

test("ํŒŒ์ผ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ", async () => {
  const uploadPath = "/uploads/user/123/profile.jpg";
  
  // ๊ฐ€์ƒ ํŒŒ์ผ ๋“ฑ๋ก
  Naite.t("mock:fs/promises:virtualFileSystem", uploadPath);
  
  // ํŒŒ์ผ ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ
  const exists = await fileService.checkExists(uploadPath);
  expect(exists).toBe(true);
  
  // Mock ์ •๋ฆฌ
  Naite.del("mock:fs/promises:virtualFileSystem");
});

test("์—ฌ๋Ÿฌ ํŒŒ์ผ ์กด์žฌ ํ™•์ธ", async () => {
  const files = [
    "/uploads/file1.pdf",
    "/uploads/file2.pdf",
    "/uploads/file3.pdf",
  ];
  
  // ์—ฌ๋Ÿฌ ํŒŒ์ผ ๋“ฑ๋ก
  for (const file of files) {
    Naite.t("mock:fs/promises:virtualFileSystem", file);
  }
  
  // ๋ฐฐ์น˜ ํ™•์ธ
  const results = await fileService.checkMultiple(files);
  expect(results.every(r => r.exists)).toBe(true);
  
  // ์ •๋ฆฌ
  Naite.del("mock:fs/promises:virtualFileSystem");
});

Naite Mock์˜ ์ž‘๋™ ์›๋ฆฌ

Naite๋Š” ํŠน๋ณ„ํ•œ ํ‚ค ํŒจํ„ด(mock:*)์„ ๊ฐ์ง€ํ•˜์—ฌ ํ•ด๋‹น ๋™์ž‘์„ ๊ฐ€๋กœ์ฑ•๋‹ˆ๋‹ค:
// Sonamu ๋‚ด๋ถ€ (์˜์‚ฌ ์ฝ”๋“œ)
async function access(path: string) {
  // Naite์—์„œ mock:fs/promises:virtualFileSystem ํ™•์ธ
  const mocked = Naite.get("mock:fs/promises:virtualFileSystem")
    .result()
    .includes(path);
  
  if (mocked) {
    // ๊ฐ€์ƒ ํŒŒ์ผ์ด ๋“ฑ๋ก๋˜์–ด ์žˆ์œผ๋ฉด ์„ฑ๊ณต
    return Promise.resolve();
  }
  
  // ์‹ค์ œ ํŒŒ์ผ ์‹œ์Šคํ…œ ํ™•์ธ
  return realAccess(path);
}

Manual Mock ๊ตฌํ˜„ํ•˜๊ธฐ

๋ณต์žกํ•œ ์™ธ๋ถ€ ์„œ๋น„์Šค๋Š” ์ง์ ‘ Mock ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

Mock ํด๋ž˜์Šค ์ƒ์„ฑ

// __mocks__/payment.service.ts
export class MockPaymentService {
  private payments: Map<string, any> = new Map();
  
  async charge(amount: number, userId: number) {
    const paymentId = `mock_${Date.now()}`;
    
    this.payments.set(paymentId, {
      id: paymentId,
      amount,
      userId,
      status: "success",
      createdAt: new Date(),
    });
    
    return {
      success: true,
      paymentId,
    };
  }
  
  async getPayment(paymentId: string) {
    return this.payments.get(paymentId) ?? null;
  }
  
  async refund(paymentId: string) {
    const payment = this.payments.get(paymentId);
    if (!payment) {
      throw new Error("๊ฒฐ์ œ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค");
    }
    
    payment.status = "refunded";
    return { success: true };
  }
  
  // ํ…Œ์ŠคํŠธ์šฉ ํ—ฌํผ
  clear() {
    this.payments.clear();
  }
}

Mock ์‚ฌ์šฉํ•˜๊ธฐ

import { test, beforeEach } from "vitest";
import { MockPaymentService } from "./__mocks__/payment.service";
import { orderService } from "../services/order.service";

let mockPayment: MockPaymentService;

beforeEach(() => {
  mockPayment = new MockPaymentService();
  // ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ Mock์œผ๋กœ ๊ต์ฒด
  orderService.paymentService = mockPayment as any;
});

test("์ฃผ๋ฌธ ๊ฒฐ์ œ", async () => {
  const order = await orderService.create({
    userId: 1,
    items: [{ productId: 1, quantity: 2 }],
    totalAmount: 50000,
  });
  
  // Mock ๊ฒฐ์ œ ์‹คํ–‰
  const result = await orderService.processPayment(order.id);
  
  expect(result.success).toBe(true);
  expect(result.paymentId).toBeDefined();
  
  // Mock์— ๊ธฐ๋ก๋œ ๊ฒฐ์ œ ํ™•์ธ
  const payment = await mockPayment.getPayment(result.paymentId);
  expect(payment?.amount).toBe(50000);
  expect(payment?.status).toBe("success");
});

test("๊ฒฐ์ œ ์‹คํŒจ ์‹œ๋‚˜๋ฆฌ์˜ค", async () => {
  // Mock์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒํ•˜๋„๋ก ์„ค์ •
  mockPayment.charge = async () => {
    throw new Error("์นด๋“œ ์Šน์ธ ์‹คํŒจ");
  };
  
  const order = await orderService.create({
    userId: 1,
    items: [{ productId: 1, quantity: 1 }],
    totalAmount: 10000,
  });
  
  await expect(
    orderService.processPayment(order.id)
  ).rejects.toThrow("์นด๋“œ ์Šน์ธ ์‹คํŒจ");
});

์‹ค์ „ ํ™œ์šฉ ์˜ˆ์ œ

์™ธ๋ถ€ API ํ˜ธ์ถœ Mocking

import { test, vi } from "vitest";
import axios from "axios";
import { weatherService } from "../services/weather.service";

vi.mock("axios");

test("๋‚ ์”จ ์ •๋ณด ์กฐํšŒ", async () => {
  // API ์‘๋‹ต Mock
  vi.mocked(axios.get).mockResolvedValue({
    data: {
      temperature: 25,
      condition: "๋ง‘์Œ",
      humidity: 60,
    },
  });
  
  const weather = await weatherService.getCurrentWeather("Seoul");
  
  expect(weather.temperature).toBe(25);
  expect(weather.condition).toBe("๋ง‘์Œ");
  expect(axios.get).toHaveBeenCalledWith(
    expect.stringContaining("Seoul")
  );
});

test("API ์—๋Ÿฌ ์ฒ˜๋ฆฌ", async () => {
  // ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
  vi.mocked(axios.get).mockRejectedValue(
    new Error("Network Error")
  );
  
  await expect(
    weatherService.getCurrentWeather("Seoul")
  ).rejects.toThrow("๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค");
});

SMS ๋ฐœ์†ก Mocking

import { test, vi } from "vitest";
import { smsService } from "../services/sms.service";
import { userService } from "../services/user.service";

test("ํšŒ์›๊ฐ€์ž… SMS ๋ฐœ์†ก", async () => {
  // SMS ๋ฐœ์†ก ํ•จ์ˆ˜ Mock
  const sendSpy = vi
    .spyOn(smsService, "send")
    .mockResolvedValue({ success: true, messageId: "mock_123" });
  
  await userService.register({
    phone: "010-1234-5678",
    name: "ํ™๊ธธ๋™",
  });
  
  // SMS ๋ฐœ์†ก ํ™•์ธ
  expect(sendSpy).toHaveBeenCalledWith({
    to: "010-1234-5678",
    message: expect.stringContaining("์ธ์ฆ๋ฒˆํ˜ธ"),
  });
  
  sendSpy.mockRestore();
});

ํŒŒ์ผ ์—…๋กœ๋“œ Mocking

import { test } from "sonamu/test";
import { Naite } from "sonamu";
import { imageService } from "../services/image.service";
import { expect } from "vitest";

test("์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ", async () => {
  const originalPath = "/tmp/upload/original.jpg";
  const thumbnailPath = "/tmp/upload/thumbnail.jpg";
  
  // ๊ฐ€์ƒ ํŒŒ์ผ ๋“ฑ๋ก
  Naite.t("mock:fs/promises:virtualFileSystem", originalPath);
  
  // ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ (์ธ๋„ค์ผ ์ƒ์„ฑ)
  await imageService.createThumbnail(originalPath, thumbnailPath);
  
  // ์ธ๋„ค์ผ๋„ ๊ฐ€์ƒ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •
  Naite.t("mock:fs/promises:virtualFileSystem", thumbnailPath);
  
  // ๋‘ ํŒŒ์ผ ๋ชจ๋‘ ์กด์žฌ ํ™•์ธ
  const exists = await imageService.checkBothExist(
    originalPath,
    thumbnailPath
  );
  expect(exists).toBe(true);
  
  // ์ •๋ฆฌ
  Naite.del("mock:fs/promises:virtualFileSystem");
});

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ Mocking

import { test, vi } from "vitest";
import { configService } from "../services/config.service";

test("API ํ‚ค ์„ค์ •", () => {
  // ํ™˜๊ฒฝ ๋ณ€์ˆ˜ Mock
  vi.stubEnv("API_KEY", "test_key_12345");
  vi.stubEnv("API_URL", "https://test.api.com");
  
  const config = configService.load();
  
  expect(config.apiKey).toBe("test_key_12345");
  expect(config.apiUrl).toBe("https://test.api.com");
  
  // ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋ณต์›
  vi.unstubAllEnvs();
});

Mocking ์ „๋žต

๊ณ„์ธต๋ณ„ Mocking

ํ…Œ์ŠคํŠธ ๋ ˆ๋ฒจ๋ณ„ Mocking:
  1. Unit ํ…Œ์ŠคํŠธ: ๋ชจ๋“  ์˜์กด์„ฑ Mock
    test("Service ๋‹จ์œ„ ํ…Œ์ŠคํŠธ", async () => {
      // Repository์™€ External API ๋ชจ๋‘ Mock
      const mockRepo = { findById: vi.fn() };
      const mockApi = { fetch: vi.fn() };
      
      const service = new UserService(mockRepo, mockApi);
      await service.getUser(1);
    });
    
  2. Integration ํ…Œ์ŠคํŠธ: ์™ธ๋ถ€ ์˜์กด์„ฑ๋งŒ Mock
    test("Service + Repository ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ", async () => {
      // External API๋งŒ Mock, DB๋Š” ์‹ค์ œ ์‚ฌ์šฉ
      vi.mock("axios");
      
      const user = await userService.getUser(1);
    });
    
  3. E2E ํ…Œ์ŠคํŠธ: Mocking ์ตœ์†Œํ™”
    test("์ „์ฒด ํ๋ฆ„ ํ…Œ์ŠคํŠธ", async () => {
      // ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜๋Š” ์ž‘์—…๋งŒ Mock (๊ฒฐ์ œ, SMS ๋“ฑ)
      vi.spyOn(paymentService, "charge").mockResolvedValue({
        success: true,
      });
      
      const response = await request(app).post("/api/orders");
    });
    

Mock ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

ํ…Œ์ŠคํŠธ์šฉ Mock ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ„๋„ ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค:
// __fixtures__/users.ts
export const mockUsers = {
  admin: {
    id: 1,
    username: "admin",
    role: "admin",
    email: "[email protected]",
  },
  user: {
    id: 2,
    username: "user",
    role: "user",
    email: "[email protected]",
  },
  guest: {
    id: 3,
    username: "guest",
    role: "guest",
    email: "[email protected]",
  },
};

// __fixtures__/api-responses.ts
export const mockApiResponses = {
  weather: {
    success: {
      temperature: 25,
      condition: "๋ง‘์Œ",
    },
    error: {
      error: "API_ERROR",
      message: "๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค",
    },
  },
  payment: {
    success: {
      success: true,
      transactionId: "txn_12345",
    },
    failure: {
      success: false,
      error: "INSUFFICIENT_FUNDS",
    },
  },
};
์‚ฌ์šฉ:
import { mockUsers } from "./__fixtures__/users";
import { mockApiResponses } from "./__fixtures__/api-responses";

test("๊ด€๋ฆฌ์ž ๊ถŒํ•œ ํ…Œ์ŠคํŠธ", async () => {
  vi.mocked(userService.findById).mockResolvedValue(mockUsers.admin);
  
  const hasPermission = await authService.checkAdmin(1);
  expect(hasPermission).toBe(true);
});

test("๋‚ ์”จ API ์„ฑ๊ณต", async () => {
  vi.mocked(axios.get).mockResolvedValue({
    data: mockApiResponses.weather.success,
  });
  
  const weather = await weatherService.get("Seoul");
  expect(weather.temperature).toBe(25);
});

์ฃผ์˜์‚ฌํ•ญ

Mocking ์‹œ ์ฃผ์˜์‚ฌํ•ญ:
  1. ๊ณผ๋„ํ•œ Mocking ์ง€์–‘: Mock์ด ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ์‹ค์ œ ๋™์ž‘๊ณผ ๊ดด๋ฆฌ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค. ๊ผญ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ Mockํ•˜์„ธ์š”.
  2. Mock ์ •๋ฆฌ: afterEach์—์„œ Mock์„ ์ •๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    afterEach(() => {
      vi.restoreAllMocks();
      Naite.del("mock:*");
    });
    
  3. Mock๊ณผ ์‹ค์ œ ๋™์ž‘ ์ผ์น˜: Mock์˜ ๋™์ž‘์ด ์‹ค์ œ ์„œ๋น„์Šค์™€ ๋‹ค๋ฅด๋ฉด ํ…Œ์ŠคํŠธ๋Š” ํ†ต๊ณผํ•ด๋„ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  4. ํƒ€์ž… ์•ˆ์ „์„ฑ: Mock์—๋„ ํƒ€์ž…์„ ๋ช…์‹œํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•˜์„ธ์š”.
    // โŒ ํƒ€์ž… ์—†์Œ
    vi.mocked(service.method).mockResolvedValue({});
    
    // โœ… ํƒ€์ž… ๋ช…์‹œ
    vi.mocked(service.method).mockResolvedValue({
      id: 1,
      name: "test",
    } as User);
    
  5. Naite Mock ๋ฒ”์œ„: mock:* ํ‚ค๋Š” ์ „์—ญ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์ข…๋ฃŒ ์‹œ ๋ฐ˜๋“œ์‹œ ์‚ญ์ œํ•˜์„ธ์š”.

๋‹ค์Œ ๋‹จ๊ณ„