๐Ÿš€ Jest๋กœ Next.js API ํ…Œ์ŠคํŠธํ•˜๊ธฐ (query-string ์‚ฝ์งˆ)

catmakerยท2024๋…„ 11์›” 14์ผ

library

๋ชฉ๋ก ๋ณด๊ธฐ
12/13
post-thumbnail

code-it ์‹ฌํ™” ๊ณผ์ •์—์„œ ๋ฐฐ์šด ํ…Œ์ŠคํŠธ๋ฅผ ๋‚ด ํ”„๋กœ์ ํŠธ์— ์ ์šฉ ์‹œ์ผœ๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค!

ํ•ต์‹ฌ์ด ๋˜๋Š” API ํ˜ธ์ถœ์— ๊ด€ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋ฐฐ์šด ์ ๋“ค์„ ๊ธฐ๋กํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

๐Ÿ“Œ ๋ชฉ์ฐจ

  1. Jest ํ…Œ์ŠคํŠธ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ
  2. ๋ชจํ‚น(Mocking)์˜ .. ์˜ˆ์ˆ 
  3. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋‹ค๋ฃจ๊ธฐ
  4. ์‹ค์ „ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ
  5. ์ฃผ์˜์‚ฌํ•ญ

๐Ÿงช Jest ํ…Œ์ŠคํŠธ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

ํ…Œ์ŠคํŠธ ๊ทธ๋ฃนํ™”์™€ ๊ฐœ๋ณ„ ํ…Œ์ŠคํŠธ

describe("getProducts", () => {
  it("should fetch featured products, async () => {
     // ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ
     })
  )}

describe ๋Š” ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋ฅผ ์ •์˜ํ•˜๊ณ , it์€ ๊ฐœ๋ณ„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ƒ๋ช…์ฃผ๊ธฐ ํ›…

beforeEach(() => {
  global.fetch = jest.fn();
});
afterEach(() => {
  jest.resetAllmocks();
});

beforeEach๋Š” ํ…Œ์ŠคํŠธ ์ „ ์ค€๋น„ ์ž‘์—…
afterEach๋Š” ํ…Œ์ŠคํŠธ ํ›„ ์ •๋ฆฌ ์ž‘์—… ์ด๋‹ค.

ํ…Œ์ŠคํŠธ ์ „์— fetch๋ฅผ ๋ชจํ‚นํ•˜๊ณ  afterEach๋ฅผ ํ†ตํ•ด ๋ชจํ‚น์„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.

์ด์œ ๋Š” ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.

๐ŸŽญ ๋ชจํ‚น(Mocking)์˜ .. ์˜ˆ์ˆ 

์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชจํ‚นํ•˜๊ธฐ

jest.mock("query-string", () => ({
  stringifyUrl: ({ url, query }: { url: string; query: any }) => {
    const urlObj = new URL(url);
    Object.entries(query).forEach(([key, value]) => {
      if (value !== undefined) {
        urlObj.searchParams.append(key, String(value));
      }
    });
    return urlObj.toString();
  },
}));

๋‚˜๋Š” query-string์„ ๋ชจํ‚นํ–ˆ๋‹ค. ์ฒ˜์Œ์—” ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€ ๋ชฐ๋ผ์„œ ์—„์ฒญ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๋Œ์•„๋‹ค๋…”๋‹ค..
๋ชจํ‚น์„ ํ•ด์•ผํ•˜๋Š” ์ด์œ ๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์ œ์–ดํ•ด์•ผํ•˜๊ณ  ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ์ œ๊ฑฐํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ํ•œ๋‹ค.

fetch API ๋ชจํ‚นํ•˜๊ธฐ

(global.fetch as jest.Mock).mockResolvedValue({
  json: () => Promise.resolve(mockProducts),
});

fetch๋ฅผ ๋ชจํ‚นํ•˜๋Š” ์ด์œ ๋Š” ์‹ค์ œ ๋„คํŠธ์›Œํฌ์˜ ์š”์ฒญ ์—†์ด ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ณ  ์—๋Ÿฌ ์ƒํ™ฉ์„ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.

๐ŸŒ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋‹ค๋ฃจ๊ธฐ

const STORE_ID = "ํ•„์š”ํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜";
process.env.NEXT_PUBLIC_API_URL = `http://localhost:3000/api/${STORE_ID}`;

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์ด์œ ๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๋ถ„๋ฆฌ์™€ ์‹ค์ œ API์™€์˜ ํ˜ผ๋™์„ ๋ฐฉ์ง€ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.

๐Ÿ’ป ์‹ค์ „ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์„ค๊ณ„

it("should fetch featured products", async () => {
  // Given: ํ…Œ์ŠคํŠธ ์ค€๋น„
  (global.fetch as jest.Mock).mockResolvedValue({
    json: () => Promise.resolve(mockProducts),
  });

  // When: ํ…Œ์ŠคํŠธ ์‹คํ–‰
  const result = await getProducts({ isFeatured: true });

  // Then: ๊ฒฐ๊ณผ ๊ฒ€์ฆ
  expect(result).toEqual(mockProducts);
  expect(fetch).toHaveBeenCalledWith(
    `http://localhost:3000/api/${STORE_ID}/products?isFeatured=true`,
    expect.any(Object)
  );
});

Given-When-Then ํŒจํ„ด

Given์€ ํ…Œ์ŠคํŠธ ์ค€๋น„ ๋‹จ๊ณ„
When์€ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ์‹คํ–‰
Then์€ ๊ฒฐ๊ณผ ๊ฒ€์ฆ

์ฃผ์˜์‚ฌํ•ญ ๐Ÿฏ

  • ๊ฐ ํ…Œ์ŠคํŠธ๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ์ƒํƒœ ๊ณต์œ ๋Š” ๊ธˆ์ง€ํ•œ๋‹ค.
  • ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์ฃผ์˜
  • ์ตœ์†Œํ•œ์˜ ๋ชจํ‚น๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์‹ค์ œ ๋™์ž‘๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๊ตฌํ˜„ํ•œ๋‹ค.
  • ํƒ€์ž… ์•ˆ์ •์„ฑ ์œ ์ง€
  • ๋ช…ํ™•ํ•œ ํ…Œ์ŠคํŠธ ์ด๋ฆ„๊ณผ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  • ์ ์ ˆํ•œ ์ฃผ์„์€ ๋„์›€์ด ๋œ๋‹ค.
profile
'์™œ?'

0๊ฐœ์˜ ๋Œ“๊ธ€