Skip to content

Commit 1941c7a

Browse files
committed
test: slogan schema 유효성 검증, formReducer 액션, handleSloganFormSubmit 제출 성공/실패/비정상 상태코드 테스트 추가
1 parent 05a5fac commit 1941c7a

File tree

3 files changed

+214
-0
lines changed

3 files changed

+214
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, it, expect } from "vitest";
2+
import { formReducer, initialFormState, FormState } from "../formReducer";
3+
4+
describe("formReducer - UPDATE_FIELD", () => {
5+
it("slogan 필드를 업데이트한다", () => {
6+
const next = formReducer(initialFormState, { type: "UPDATE_FIELD", field: "slogan", value: "광주인재" });
7+
expect(next.formValues.slogan).toBe("광주인재");
8+
});
9+
10+
it("grade 필드를 업데이트한다", () => {
11+
const next = formReducer(initialFormState, { type: "UPDATE_FIELD", field: "grade", value: "3" });
12+
expect(next.formValues.grade).toBe("3");
13+
});
14+
15+
it("업데이트한 필드 외 나머지 필드는 변경되지 않는다", () => {
16+
const next = formReducer(initialFormState, { type: "UPDATE_FIELD", field: "slogan", value: "광주" });
17+
expect(next.formValues.description).toBe("");
18+
expect(next.formValues.school).toBe("");
19+
});
20+
});
21+
22+
describe("formReducer - SET_SUBMITTING", () => {
23+
it("isSubmitting을 true로 설정한다", () => {
24+
const next = formReducer(initialFormState, { type: "SET_SUBMITTING", value: true });
25+
expect(next.isSubmitting).toBe(true);
26+
});
27+
28+
it("isSubmitting을 false로 설정한다", () => {
29+
const state: FormState = { ...initialFormState, isSubmitting: true };
30+
const next = formReducer(state, { type: "SET_SUBMITTING", value: false });
31+
expect(next.isSubmitting).toBe(false);
32+
});
33+
});
34+
35+
describe("formReducer - SET_SUBMITTED", () => {
36+
it("isSubmitted를 true로 설정한다", () => {
37+
const next = formReducer(initialFormState, { type: "SET_SUBMITTED", value: true });
38+
expect(next.isSubmitted).toBe(true);
39+
});
40+
41+
it("isSubmitted를 false로 설정한다", () => {
42+
const state: FormState = { ...initialFormState, isSubmitted: true };
43+
const next = formReducer(state, { type: "SET_SUBMITTED", value: false });
44+
expect(next.isSubmitted).toBe(false);
45+
});
46+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { describe, it, expect, vi, beforeEach } from "vitest";
2+
import { handleSloganFormSubmit } from "../handleSloganFormSubmit";
3+
4+
vi.mock("@/entities/slogan/api/postSlogan", () => ({ postSlogan: vi.fn() }));
5+
vi.mock("sonner", () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
6+
7+
import { postSlogan } from "@/entities/slogan/api/postSlogan";
8+
import { toast } from "sonner";
9+
10+
const mockPostSlogan = vi.mocked(postSlogan);
11+
const mockSuccess = vi.mocked(toast.success);
12+
const mockError = vi.mocked(toast.error);
13+
14+
const VALID_VALUES = {
15+
slogan: "광주인재페스티벌",
16+
description: "슬로건 설명입니다.",
17+
school: "광주고등학교",
18+
name: "홍길동",
19+
grade: "3",
20+
classroom: "5",
21+
phone_number: "01012345678",
22+
};
23+
24+
beforeEach(() => {
25+
vi.clearAllMocks();
26+
});
27+
28+
describe("handleSloganFormSubmit - 제출 성공", () => {
29+
it("status 200이면 toast.success를 호출하고 응답을 반환한다", async () => {
30+
const res = { status: 200, data: {} };
31+
mockPostSlogan.mockResolvedValue(res as any);
32+
const result = await handleSloganFormSubmit(VALID_VALUES);
33+
expect(mockSuccess).toHaveBeenCalledWith("슬로건이 제출되었습니다.");
34+
expect(result).toBe(res);
35+
});
36+
37+
it("status 201이면 toast.success를 호출하고 응답을 반환한다", async () => {
38+
const res = { status: 201, data: {} };
39+
mockPostSlogan.mockResolvedValue(res as any);
40+
const result = await handleSloganFormSubmit(VALID_VALUES);
41+
expect(mockSuccess).toHaveBeenCalledWith("슬로건이 제출되었습니다.");
42+
expect(result).toBe(res);
43+
});
44+
});
45+
46+
describe("handleSloganFormSubmit - 제출 실패", () => {
47+
it("API가 예외를 던지면 toast.error를 호출한다", async () => {
48+
mockPostSlogan.mockRejectedValue(new Error("network error"));
49+
await handleSloganFormSubmit(VALID_VALUES);
50+
expect(mockError).toHaveBeenCalledWith("슬로건 제출에 실패했습니다.");
51+
});
52+
53+
it("비정상 상태코드(400)이면 toast.error를 호출한다", async () => {
54+
mockPostSlogan.mockResolvedValue({ status: 400, data: {} } as any);
55+
await handleSloganFormSubmit(VALID_VALUES);
56+
expect(mockError).toHaveBeenCalledWith("슬로건 제출에 실패했습니다.");
57+
});
58+
59+
it("비정상 상태코드(500)이면 toast.error를 호출한다", async () => {
60+
mockPostSlogan.mockResolvedValue({ status: 500, data: {} } as any);
61+
await handleSloganFormSubmit(VALID_VALUES);
62+
expect(mockError).toHaveBeenCalledWith("슬로건 제출에 실패했습니다.");
63+
});
64+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { describe, it, expect } from "vitest";
2+
import {
3+
sloganNameSchema,
4+
sloganDescriptionSchema,
5+
gradeSchema,
6+
classroomSchema,
7+
sloganSchema,
8+
} from "../schema";
9+
10+
describe("sloganNameSchema", () => {
11+
it("1자 이상이면 유효하다", () => {
12+
expect(sloganNameSchema.safeParse("광주인재페스티벌").success).toBe(true);
13+
});
14+
15+
it("빈 값이면 실패한다", () => {
16+
const result = sloganNameSchema.safeParse("");
17+
expect(result.success).toBe(false);
18+
expect(result.error?.errors[0].message).toBe("슬로건을 입력해주세요.");
19+
});
20+
21+
it("100자를 초과하면 실패한다", () => {
22+
expect(sloganNameSchema.safeParse("a".repeat(101)).success).toBe(false);
23+
});
24+
});
25+
26+
describe("sloganDescriptionSchema", () => {
27+
it("1자 이상이면 유효하다", () => {
28+
expect(sloganDescriptionSchema.safeParse("슬로건 설명입니다.").success).toBe(true);
29+
});
30+
31+
it("빈 값이면 실패한다", () => {
32+
const result = sloganDescriptionSchema.safeParse("");
33+
expect(result.success).toBe(false);
34+
expect(result.error?.errors[0].message).toBe("슬로건 설명을 입력해주세요.");
35+
});
36+
37+
it("1000자를 초과하면 실패한다", () => {
38+
expect(sloganDescriptionSchema.safeParse("a".repeat(1001)).success).toBe(false);
39+
});
40+
});
41+
42+
describe("gradeSchema", () => {
43+
it("숫자 문자열이면 유효하다", () => {
44+
expect(gradeSchema.safeParse("3").success).toBe(true);
45+
});
46+
47+
it("빈 값이면 실패한다", () => {
48+
expect(gradeSchema.safeParse("").success).toBe(false);
49+
});
50+
51+
it("숫자가 아닌 문자가 포함되면 실패한다", () => {
52+
const result = gradeSchema.safeParse("3학년");
53+
expect(result.success).toBe(false);
54+
expect(result.error?.errors[0].message).toBe("학년은 숫자만 입력할 수 있습니다.");
55+
});
56+
});
57+
58+
describe("classroomSchema", () => {
59+
it("숫자 문자열이면 유효하다", () => {
60+
expect(classroomSchema.safeParse("5").success).toBe(true);
61+
});
62+
63+
it("빈 값이면 실패한다", () => {
64+
expect(classroomSchema.safeParse("").success).toBe(false);
65+
});
66+
67+
it("숫자가 아닌 문자가 포함되면 실패한다", () => {
68+
const result = classroomSchema.safeParse("5반");
69+
expect(result.success).toBe(false);
70+
expect(result.error?.errors[0].message).toBe("반은 숫자만 입력할 수 있습니다.");
71+
});
72+
});
73+
74+
describe("sloganSchema", () => {
75+
const valid = {
76+
slogan: "광주인재페스티벌",
77+
description: "슬로건 설명입니다.",
78+
school: "광주고등학교",
79+
name: "홍길동",
80+
grade: "3",
81+
classroom: "5",
82+
phone_number: "01012345678",
83+
};
84+
85+
it("모든 필드가 올바르면 유효하다", () => {
86+
expect(sloganSchema.safeParse(valid).success).toBe(true);
87+
});
88+
89+
it("슬로건이 빈 값이면 실패한다", () => {
90+
expect(sloganSchema.safeParse({ ...valid, slogan: "" }).success).toBe(false);
91+
});
92+
93+
it("학년에 숫자가 아닌 값이 있으면 실패한다", () => {
94+
expect(sloganSchema.safeParse({ ...valid, grade: "삼" }).success).toBe(false);
95+
});
96+
97+
it("반에 숫자가 아닌 값이 있으면 실패한다", () => {
98+
expect(sloganSchema.safeParse({ ...valid, classroom: "오" }).success).toBe(false);
99+
});
100+
101+
it("전화번호 형식이 잘못되면 실패한다", () => {
102+
expect(sloganSchema.safeParse({ ...valid, phone_number: "010-1234-5678" }).success).toBe(false);
103+
});
104+
});

0 commit comments

Comments
 (0)