TDD 실습 (2)

노력하는백엔드·2026년 3월 8일

이론 정리

목록 보기
12/14

메모 단건 조회

Red

  • 메모 조회에 대한 필수 값 누락에 대한 테스트 진행

    • 단건 조회

      • id 필요
      • 없는 id 에러, 범위 검증
      • 객체 1개 반환
      • 코드
      const { readMemo } = require("../../src/memo/readMemo");
      
      describe("readMemo", () => {
        test("정상 id이면 memo 단건 조회에 성공한다", async () => {
          const result = await readMemo(1);
      
          expect(result).toMatchObject({
            id: 1,
            title: "TDD 메모 작성",
            period: {
              startDate: "2026-03-06",
              endDate: "2026-03-10",
            },
            content: "첫 번째 메모",
            priority: 3,
            isSuccess: false,
          });
        });
      
        test("id가 없으면 에러 발생", async () => {
          await expect(readMemo()).rejects.toThrow("id");
        });
      
        test("id가 null이면 에러 발생", async () => {
          await expect(readMemo(null)).rejects.toThrow("id");
        });
      
        test("id가 문자열이면 에러 발생", async () => {
          await expect(readMemo("1")).rejects.toThrow("id");
        });
      
        test("id가 0이면 에러 발생", async () => {
          await expect(readMemo(0)).rejects.toThrow("id");
        });
      
        test("id가 음수이면 에러 발생", async () => {
          await expect(readMemo(-1)).rejects.toThrow("id");
        });
      
        test("존재하지 않는 id이면 에러 발생", async () => {
          await expect(readMemo(999)).rejects.toThrow("memo");
        });
      });
  • 실행 로그

    hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test
    
    > tdd_study@1.0.0 test
    > jest
    
     FAIL  __test__/memo/readMemo.test.js
      ● readMemo › 정상 id이면 memo 단건 조회에 성공한다
    
        TypeError: readMemo is not a function
    
          3 | describe("readMemo", () => {
          4 |   test("정상 id이면 memo 단건 조회에 성공한다", async () => {
        > 5 |     const result = await readMemo(1);
            |                          ^
          6 |
          7 |     expect(result).toMatchObject({
          8 |       id: 1,
    
          at Object.readMemo (__test__/memo/readMemo.test.js:5:26)
    
      ● readMemo › id가 없으면 에러 발생
    
        TypeError: readMemo is not a function
    
          19 |
          20 |   test("id가 없으면 에러 발생", async () => {
        > 21 |     await expect(readMemo()).rejects.toThrow("id");
             |                  ^
          22 |   });
          23 |
          24 |   test("id가 null이면 에러 발생", async () => {
    
          at Object.readMemo (__test__/memo/readMemo.test.js:21:18)
    
      ● readMemo › id가 null이면 에러 발생
    
        TypeError: readMemo is not a function
    
          23 |
          24 |   test("id가 null이면 에러 발생", async () => {
        > 25 |     await expect(readMemo(null)).rejects.toThrow("id");
             |                  ^
          26 |   });
          27 |
          28 |   test("id가 문자열이면 에러 발생", async () => {
    
          at Object.readMemo (__test__/memo/readMemo.test.js:25:18)
    
      ● readMemo › id가 문자열이면 에러 발생
    
        TypeError: readMemo is not a function
    
          27 |
          28 |   test("id가 문자열이면 에러 발생", async () => {
        > 29 |     await expect(readMemo("1")).rejects.toThrow("id");
             |                  ^
          30 |   });
          31 |
          32 |   test("id가 0이면 에러 발생", async () => {
    
          at Object.readMemo (__test__/memo/readMemo.test.js:29:18)
    
      ● readMemo › id가 0이면 에러 발생
    
        TypeError: readMemo is not a function
    
          31 |
          32 |   test("id가 0이면 에러 발생", async () => {
        > 33 |     await expect(readMemo(0)).rejects.toThrow("id");
             |                  ^
          34 |   });
          35 |
          36 |   test("id가 음수이면 에러 발생", async () => {
    
          at Object.readMemo (__test__/memo/readMemo.test.js:33:18)
    
      ● readMemo › id가 음수이면 에러 발생
    
        TypeError: readMemo is not a function
    
          35 |
          36 |   test("id가 음수이면 에러 발생", async () => {
        > 37 |     await expect(readMemo(-1)).rejects.toThrow("id");
             |                  ^
          38 |   });
          39 |
          40 |   test("존재하지 않는 id이면 에러 발생", async () => {
    
          at Object.readMemo (__test__/memo/readMemo.test.js:37:18)
    
      ● readMemo › 존재하지 않는 id이면 에러 발생
    
        TypeError: readMemo is not a function
    
          39 |
          40 |   test("존재하지 않는 id이면 에러 발생", async () => {
        > 41 |     await expect(readMemo(999)).rejects.toThrow("memo");
             |                  ^
          42 |   });
          43 | });
          44 |
    
          at Object.readMemo (__test__/memo/readMemo.test.js:41:18)
    
     PASS  __test__/memo/createMemo.test.js
    
    Test Suites: 1 failed, 1 passed, 2 total
    Tests:       7 failed, 16 passed, 23 total
    Snapshots:   0 total
    Time:        0.498 s, estimated 1 s
    Ran all test suites.
  • 리스트 조회

    • page 필요
    • pageSize 필요
    • page 타입 검증
    • pageSize 타입 검증
    • page 범위 검증 (1 이상)
    • pageSize 범위 검증 (1 이상)
    • isSuccess 필터
      • boolean 타입 검증
      • true → 성공 메모만 조회
      • false → 진행 중 메모 조회
    • priority 필터
      • 숫자 타입 검증
      • 범위 검증 (1 ~ 5)
      • priority 값에 해당하는 메모만 조회
    • 코드
      const { listMemo } = require("../../src/memo/readMemo");
    
      describe("listMemo", () => {
        test("page와 pageSize가 정상 입력이면 memo 목록 조회에 성공한다", async () => {
          const result = await listMemo({
            page: 1,
            pageSize: 10,
          });
    
          expect(Array.isArray(result)).toBe(true);
        });
    
        test("page가 없으면 에러 발생", async () => {
          await expect(
            listMemo({
              pageSize: 10,
            }),
          ).rejects.toThrow("page");
        });
    
        test("pageSize가 없으면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
            }),
          ).rejects.toThrow("pageSize");
        });
    
        test("page가 숫자가 아니면 에러 발생", async () => {
          await expect(
            listMemo({
              page: "1",
              pageSize: 10,
            }),
          ).rejects.toThrow("page");
        });
    
        test("pageSize가 숫자가 아니면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
              pageSize: "10",
            }),
          ).rejects.toThrow("pageSize");
        });
    
        test("page가 1보다 작으면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 0,
              pageSize: 10,
            }),
          ).rejects.toThrow("page");
        });
    
        test("pageSize가 1보다 작으면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
              pageSize: 0,
            }),
          ).rejects.toThrow("pageSize");
        });
    
        test("isSuccess가 boolean이 아니면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
              pageSize: 10,
              isSuccess: "true",
            }),
          ).rejects.toThrow("isSuccess");
        });
    
        test("isSuccess가 true이면 성공한 메모만 조회", async () => {
          const result = await listMemo({
            page: 1,
            pageSize: 10,
            isSuccess: true,
          });
    
          expect(Array.isArray(result)).toBe(true);
          result.forEach((memo) => {
            expect(memo.isSuccess).toBe(true);
          });
        });
    
        test("isSuccess가 false이면 진행 중 메모만 조회", async () => {
          const result = await listMemo({
            page: 1,
            pageSize: 10,
            isSuccess: false,
          });
    
          expect(Array.isArray(result)).toBe(true);
          result.forEach((memo) => {
            expect(memo.isSuccess).toBe(false);
          });
        });
    
        test("priority가 숫자가 아니면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
              pageSize: 10,
              priority: "3",
            }),
          ).rejects.toThrow("priority");
        });
    
        test("priority가 1보다 작으면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
              pageSize: 10,
              priority: 0,
            }),
          ).rejects.toThrow("priority");
        });
    
        test("priority가 5보다 크면 에러 발생", async () => {
          await expect(
            listMemo({
              page: 1,
              pageSize: 10,
              priority: 6,
            }),
          ).rejects.toThrow("priority");
        });
    
        test("priority가 주어지면 해당 priority 메모만 조회", async () => {
          const result = await listMemo({
            page: 1,
            pageSize: 10,
            priority: 3,
          });
    
          expect(Array.isArray(result)).toBe(true);
          result.forEach((memo) => {
            expect(memo.priority).toBe(3);
          });
        });
      });
  • 실행 로그

      hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test
    
    > tdd_study@1.0.0 test
    > jest
    
     PASS  __test__/memo/createMemo.test.js
     PASS  __test__/memo/readMemo.test.js
     FAIL  __test__/memo/readMemoList.test.js
      ● listMemo › page와 pageSize가 정상 입력이면 memo 목록 조회에 성공한다
    
        TypeError: listMemo is not a function
    
          3 | describe("listMemo", () => {
          4 |   test("page와 pageSize가 정상 입력이면 memo 목록 조회에 성공한다", async () => {
        > 5 |     const result = await listMemo({
            |                          ^
          6 |       page: 1,
          7 |       pageSize: 10,
          8 |     });
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:5:26)
    
      ● listMemo › page가 없으면 에러 발생
    
        TypeError: listMemo is not a function
    
          13 |   test("page가 없으면 에러 발생", async () => {
          14 |     await expect(
        > 15 |       listMemo({
             |       ^
          16 |         pageSize: 10,
          17 |       }),
          18 |     ).rejects.toThrow("page");
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:15:7)
    
      ● listMemo › pageSize가 없으면 에러 발생
    
        TypeError: listMemo is not a function
    
          21 |   test("pageSize가 없으면 에러 발생", async () => {
          22 |     await expect(
        > 23 |       listMemo({
             |       ^
          24 |         page: 1,
          25 |       }),
          26 |     ).rejects.toThrow("pageSize");
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:23:7)
    
      ● listMemo › page가 숫자가 아니면 에러 발생
    
        TypeError: listMemo is not a function
    
          29 |   test("page가 숫자가 아니면 에러 발생", async () => {
          30 |     await expect(
        > 31 |       listMemo({
             |       ^
          32 |         page: "1",
          33 |         pageSize: 10,
          34 |       }),
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:31:7)
    
      ● listMemo › pageSize가 숫자가 아니면 에러 발생
    
        TypeError: listMemo is not a function
    
          38 |   test("pageSize가 숫자가 아니면 에러 발생", async () => {
          39 |     await expect(
        > 40 |       listMemo({
             |       ^
          41 |         page: 1,
          42 |         pageSize: "10",
          43 |       }),
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:40:7)
    
      ● listMemo › page가 1보다 작으면 에러 발생
    
        TypeError: listMemo is not a function
    
          47 |   test("page가 1보다 작으면 에러 발생", async () => {
          48 |     await expect(
        > 49 |       listMemo({
             |       ^
          50 |         page: 0,
          51 |         pageSize: 10,
          52 |       }),
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:49:7)
    
      ● listMemo › pageSize가 1보다 작으면 에러 발생
    
        TypeError: listMemo is not a function
    
          56 |   test("pageSize가 1보다 작으면 에러 발생", async () => {
          57 |     await expect(
        > 58 |       listMemo({
             |       ^
          59 |         page: 1,
          60 |         pageSize: 0,
          61 |       }),
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:58:7)
    
      ● listMemo › isSuccess가 boolean이 아니면 에러 발생
    
        TypeError: listMemo is not a function
    
          65 |   test("isSuccess가 boolean이 아니면 에러 발생", async () => {
          66 |     await expect(
        > 67 |       listMemo({
             |       ^
          68 |         page: 1,
          69 |         pageSize: 10,
          70 |         isSuccess: "true",
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:67:7)
    
      ● listMemo › isSuccess가 true이면 성공한 메모만 조회
    
        TypeError: listMemo is not a function
    
          74 |
          75 |   test("isSuccess가 true이면 성공한 메모만 조회", async () => {
        > 76 |     const result = await listMemo({
             |                          ^
          77 |       page: 1,
          78 |       pageSize: 10,
          79 |       isSuccess: true,
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:76:26)
    
      ● listMemo › isSuccess가 false이면 진행 중 메모만 조회
    
        TypeError: listMemo is not a function
    
          87 |
          88 |   test("isSuccess가 false이면 진행 중 메모만 조회", async () => {
        > 89 |     const result = await listMemo({
             |                          ^
          90 |       page: 1,
          91 |       pageSize: 10,
          92 |       isSuccess: false,
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:89:26)
    
      ● listMemo › priority가 숫자가 아니면 에러 발생
    
        TypeError: listMemo is not a function
    
          101 |   test("priority가 숫자가 아니면 에러 발생", async () => {
          102 |     await expect(
        > 103 |       listMemo({
              |       ^
          104 |         page: 1,
          105 |         pageSize: 10,
          106 |         priority: "3",
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:103:7)
    
      ● listMemo › priority가 1보다 작으면 에러 발생
    
        TypeError: listMemo is not a function
    
          111 |   test("priority가 1보다 작으면 에러 발생", async () => {
          112 |     await expect(
        > 113 |       listMemo({
              |       ^
          114 |         page: 1,
          115 |         pageSize: 10,
          116 |         priority: 0,
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:113:7)
    
      ● listMemo › priority가 5보다 크면 에러 발생
    
        TypeError: listMemo is not a function
    
          121 |   test("priority가 5보다 크면 에러 발생", async () => {
          122 |     await expect(
        > 123 |       listMemo({
              |       ^
          124 |         page: 1,
          125 |         pageSize: 10,
          126 |         priority: 6,
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:123:7)
    
      ● listMemo › priority가 주어지면 해당 priority 메모만 조회
    
        TypeError: listMemo is not a function
    
          130 |
          131 |   test("priority가 주어지면 해당 priority 메모만 조회", async () => {
        > 132 |     const result = await listMemo({
              |                          ^
          133 |       page: 1,
          134 |       pageSize: 10,
          135 |       priority: 3,
    
          at Object.listMemo (__test__/memo/readMemoList.test.js:132:26)
    
    Test Suites: 1 failed, 2 passed, 3 total
    Tests:       14 failed, 23 passed, 37 total
    Snapshots:   0 total
    Time:        0.488 s, estimated 1 s
    Ran all test suites.

Green

  • 단건 조회

    • 코드

      async function readMemo(id) {
        if (typeof id !== "number" || id <= 0) {
          throw new Error("id");
        }
      
        const mockMemos = [
          {
            id: 1,
            title: "TDD 메모 작성",
            period: {
              startDate: "2026-03-06",
              endDate: "2026-03-10",
            },
            content: "조회 테스트용 메모",
            priority: 3,
            isSuccess: false,
          },
          {
            id: 2,
            title: "두 번째 메모",
            period: {
              startDate: "2026-03-07",
              endDate: "2026-03-12",
            },
            content: "두 번째 조회 테스트용 메모",
            priority: 2,
            isSuccess: false,
          },
        ];
      
        const memo = mockMemos.find((item) => item.id === id);
      
        if (!memo) {
          throw new Error("memo");
        }
      
        return memo;
      }
      
      module.exports = { readMemo };
  • 리스트 조회

    • 코드

      async function readMemo(id) {
        if (typeof id !== "number" || id <= 0) {
          throw new Error("id");
        }
      
        const mockMemos = [
          {
            id: 1,
            title: "TDD 메모 작성",
            period: {
              startDate: "2026-03-06",
              endDate: "2026-03-10",
            },
            content: "조회 테스트용 메모",
            priority: 3,
            isSuccess: false,
          },
          {
            id: 2,
            title: "두 번째 메모",
            period: {
              startDate: "2026-03-07",
              endDate: "2026-03-12",
            },
            content: "두 번째 조회 테스트용 메모",
            priority: 2,
            isSuccess: false,
          },
        ];
      
        const memo = mockMemos.find((item) => item.id === id);
      
        if (!memo) {
          throw new Error("memo");
        }
      
        return memo;
      }
      async function listMemo(query) {
        const { page, pageSize, isSuccess, priority } = query;
      
        if (typeof page !== "number" || page < 1) {
          throw new Error("page");
        }
      
        if (typeof pageSize !== "number" || pageSize < 1) {
          throw new Error("pageSize");
        }
      
        if (isSuccess !== undefined && typeof isSuccess !== "boolean") {
          throw new Error("isSuccess");
        }
      
        if (priority !== undefined) {
          if (typeof priority !== "number" || priority < 1 || priority > 5) {
            throw new Error("priority");
          }
        }
      
        const mockMemos = [
          {
            id: 1,
            title: "TDD 메모 작성",
            period: {
              startDate: "2026-03-06",
              endDate: "2026-03-10",
            },
            content: "첫 번째 메모",
            priority: 3,
            isSuccess: false,
          },
          {
            id: 2,
            title: "두 번째 메모",
            period: {
              startDate: "2026-03-07",
              endDate: "2026-03-12",
            },
            content: "두 번째 내용",
            priority: 2,
            isSuccess: true,
          },
          {
            id: 3,
            title: "세 번째 메모",
            period: {
              startDate: "2026-03-08",
              endDate: "2026-03-15",
            },
            content: "세 번째 내용",
            priority: 3,
            isSuccess: true,
          },
          {
            id: 4,
            title: "네 번째 메모",
            period: {
              startDate: "2026-03-09",
              endDate: "2026-03-18",
            },
            content: "네 번째 내용",
            priority: 1,
            isSuccess: false,
          },
        ];
      
        let result = mockMemos;
      
        if (isSuccess !== undefined) {
          result = result.filter((memo) => memo.isSuccess === isSuccess);
        }
      
        if (priority !== undefined) {
          result = result.filter((memo) => memo.priority === priority);
        }
      
        const startIndex = (page - 1) * pageSize;
        const endIndex = startIndex + pageSize;
      
        return result.slice(startIndex, endIndex);
      }
      
      module.exports = { readMemo, listMemo };
  • 실행 로그

    • 단건 조회

        hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test
      
        > tdd_study@1.0.0 test
        > jest
      
         PASS  __test__/memo/readMemo.test.js
         PASS  __test__/memo/createMemo.test.js
      
        Test Suites: 2 passed, 2 total
        Tests:       23 passed, 23 total
        Snapshots:   0 total
        Time:        0.411 s, estimated 1 s
        Ran all test suites.
    • 리스트 조회

        hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test
      
      > tdd_study@1.0.0 test
      > jest
      
       PASS  __test__/memo/readMemoList.test.js
       PASS  __test__/memo/createMemo.test.js
       PASS  __test__/memo/readMemo.test.js
      
      Test Suites: 3 passed, 3 total
      Tests:       37 passed, 37 total
      Snapshots:   0 total
      Time:        0.364 s, estimated 1 s
      Ran all test suites.

blue

  • 코드

      const mockMemos = [
      {
        id: 1,
        title: "TDD 메모 작성",
        period: {
          startDate: "2026-03-06",
          endDate: "2026-03-10",
        },
        content: "첫 번째 메모",
        priority: 3,
        isSuccess: false,
      },
      {
        id: 2,
        title: "두 번째 메모",
        period: {
          startDate: "2026-03-07",
          endDate: "2026-03-12",
        },
        content: "두 번째 내용",
        priority: 2,
        isSuccess: true,
      },
      {
        id: 3,
        title: "세 번째 메모",
        period: {
          startDate: "2026-03-08",
          endDate: "2026-03-15",
        },
        content: "세 번째 내용",
        priority: 3,
        isSuccess: true,
      },
      {
        id: 4,
        title: "네 번째 메모",
        period: {
          startDate: "2026-03-09",
          endDate: "2026-03-18",
        },
        content: "네 번째 내용",
        priority: 1,
        isSuccess: false,
      },
    ];
    
    function validatePage(page) {
      if (typeof page !== "number" || page < 1) {
        throw new Error("page");
      }
    }
    
    function validatePageSize(pageSize) {
      if (typeof pageSize !== "number" || pageSize < 1) {
        throw new Error("pageSize");
      }
    }
    
    function validateIsSuccess(isSuccess) {
      if (isSuccess !== undefined && typeof isSuccess !== "boolean") {
        throw new Error("isSuccess");
      }
    }
    
    function validatePriority(priority) {
      if (priority !== undefined) {
        if (typeof priority !== "number" || priority < 1 || priority > 5) {
          throw new Error("priority");
        }
      }
    }
    
    function filterByIsSuccess(memos, isSuccess) {
      if (isSuccess === undefined) {
        return memos;
      }
    
      return memos.filter((memo) => memo.isSuccess === isSuccess);
    }
    
    function filterByPriority(memos, priority) {
      if (priority === undefined) {
        return memos;
      }
    
      return memos.filter((memo) => memo.priority === priority);
    }
    
    async function readMemo(id) {
      if (typeof id !== "number" || id <= 0) {
        throw new Error("id");
      }
    
      const memo = mockMemos.find((item) => item.id === id);
    
      if (!memo) {
        throw new Error("memo");
      }
    
      return memo;
    }
    
    async function listMemo(query) {
      const { page, pageSize, isSuccess, priority } = query;
    
      validatePage(page);
      validatePageSize(pageSize);
      validateIsSuccess(isSuccess);
      validatePriority(priority);
    
      let result = mockMemos;
    
      result = filterByIsSuccess(result, isSuccess);
      result = filterByPriority(result, priority);
    
      const startIndex = (page - 1) * pageSize;
      const endIndex = startIndex + pageSize;
    
      return result.slice(startIndex, endIndex);
    }
    
    module.exports = { readMemo, listMemo };
    
  • 실행 로그

    hongseongchae@hongseongchaeui-MacBookPro tdd_study % npm test
    
    > tdd_study@1.0.0 test
    > jest
    
     PASS  __test__/memo/readMemo.test.js
     PASS  __test__/memo/readMemoList.test.js
     PASS  __test__/memo/createMemo.test.js
    
    Test Suites: 3 passed, 3 total
    Tests:       37 passed, 37 total
    Snapshots:   0 total
    Time:        0.647 s, estimated 1 s
    Ran all test suites.
profile
열심히 노력하는 백엔드입니다.

0개의 댓글