Fixture μμ± κ°μ
createFixtureLoader
νμ
μμ ν μ μ μλμμ± μ§μ
μ¬μ¬μ© κ°λ₯
ν λ² μ μ μ¬λ¬ ν
μ€νΈ 곡μ
κ΄κ³ μ²λ¦¬
BelongsTo μλ ν΄κ²° μ€μ²©λ λ°μ΄ν°
μ νμ λ‘λ©
νμν κ²λ§ λΉ λ₯Έ ν
μ€νΈ
createFixtureLoader
기본 ꡬ쑰
// api/src/testing/fixture.ts
import { createFixtureLoader } from "sonamu/test";
import { UserModel } from "@/models/user.model";
import { PostModel } from "@/models/post.model";
export const loadFixtures = createFixtureLoader({
// Fixture μ΄λ¦: λ‘λ ν¨μ
user01: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "john",
email: "john@example.com",
password: "password123",
});
return user;
},
admin: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "admin",
email: "admin@example.com",
password: "admin123",
role: "admin",
});
return user;
},
});
νμ μ μ
/**
* Fixture Loader Factory
*
* ν
μ€νΈμμ μ¬μ©ν fixtureλ₯Ό λ‘λνλ ν¨μλ₯Ό μμ±
*/
export function createFixtureLoader<T extends Record<string, () => Promise<unknown>>>(loaders: T) {
return async function loadFixtures<K extends keyof T>(
names: K[],
): Promise<{ [P in K]: Awaited<ReturnType<T[P]>> }> {
return Object.fromEntries(
await Promise.all(names.map(async (name) => [name, await loaders[name]()])),
);
};
}
- νμ μμ : TypeScriptκ° fixture μ΄λ¦κ³Ό λ°ν νμ μ μΆλ‘
- λ³λ ¬ λ‘λ©:
Promise.allλ‘ μ¬λ¬ fixture λμ λ‘λ - μ νμ : νμν fixtureλ§ λ‘λ
μ€μ μμ
λ¨μ Fixtures
// api/src/testing/fixture.ts
import { createFixtureLoader } from "sonamu/test";
import { UserModel } from "@/models/user.model";
export const loadFixtures = createFixtureLoader({
// μΌλ° μ¬μ©μ
guestUser: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "guest",
email: "guest@example.com",
password: "password",
role: "guest",
});
return user;
},
// ν리미μ μ¬μ©μ
premiumUser: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "premium",
email: "premium@example.com",
password: "password",
role: "premium",
subscription_expires_at: new Date("2025-12-31"),
});
return user;
},
// κ΄λ¦¬μ
adminUser: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "admin",
email: "admin@example.com",
password: "password",
role: "admin",
});
return user;
},
});
κ΄κ³κ° μλ Fixtures
// api/src/testing/fixture.ts
import { createFixtureLoader } from "sonamu/test";
import { UserModel } from "@/models/user.model";
import { PostModel } from "@/models/post.model";
import { CommentModel } from "@/models/comment.model";
export const loadFixtures = createFixtureLoader({
// κΈ°λ³Έ μ¬μ©μ
author: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "author",
email: "author@example.com",
password: "password",
});
return user;
},
// μμ±μμ κ²μκΈ (author fixture μ¬μ¬μ©)
authorPost: async () => {
// λ€λ₯Έ fixture λ‘λ
const { author } = await loadFixtures(["author"]);
const postModel = new PostModel();
const { post } = await postModel.create({
title: "Author's First Post",
content: "This is the content",
author_id: author.id, // κ΄κ³ μ€μ
});
return post;
},
// κ²μκΈμ λκΈ (authorPost fixture μ¬μ¬μ©)
postComment: async () => {
const { author, authorPost } = await loadFixtures(["author", "authorPost"]);
const commentModel = new CommentModel();
const { comment } = await commentModel.create({
content: "Great post!",
post_id: authorPost.id,
author_id: author.id,
});
return comment;
},
});
μ¬λ¬ λ°μ΄ν° μμ±
export const loadFixtures = createFixtureLoader({
// 10λͺ
μ μ¬μ©μ
users10: async () => {
const userModel = new UserModel();
const users = [];
for (let i = 1; i <= 10; i++) {
const { user } = await userModel.create({
username: `user${i}`,
email: `user${i}@example.com`,
password: "password",
});
users.push(user);
}
return users;
},
// μ¬μ©μμ κ·Έμ κ²μκΈλ€
userWith5Posts: async () => {
const userModel = new UserModel();
const postModel = new PostModel();
const { user } = await userModel.create({
username: "blogger",
email: "blogger@example.com",
password: "password",
});
const posts = [];
for (let i = 1; i <= 5; i++) {
const { post } = await postModel.create({
title: `Post ${i}`,
content: `Content ${i}`,
author_id: user.id,
});
posts.push(post);
}
return { user, posts };
},
});
볡μ‘ν μλ리μ€
export const loadFixtures = createFixtureLoader({
// μμ ν λΈλ‘κ·Έ μλ리μ€
blogScenario: async () => {
const userModel = new UserModel();
const postModel = new PostModel();
const commentModel = new CommentModel();
// 1. μμ±μ μμ±
const { user: author } = await userModel.create({
username: "author",
email: "author@example.com",
password: "password",
});
// 2. λ
μλ€ μμ±
const readers = await Promise.all([
userModel.create({
username: "reader1",
email: "reader1@example.com",
password: "password",
}),
userModel.create({
username: "reader2",
email: "reader2@example.com",
password: "password",
}),
]);
// 3. κ²μκΈ μμ±
const { post } = await postModel.create({
title: "Popular Post",
content: "This post has many comments",
author_id: author.id,
});
// 4. λκΈ μμ±
const comments = await Promise.all([
commentModel.create({
content: "First comment",
post_id: post.id,
author_id: readers[0].user.id,
}),
commentModel.create({
content: "Second comment",
post_id: post.id,
author_id: readers[1].user.id,
}),
commentModel.create({
content: "Author reply",
post_id: post.id,
author_id: author.id,
}),
]);
return {
author,
readers: readers.map((r) => r.user),
post,
comments: comments.map((c) => c.comment),
};
},
});
ν μ€νΈμμ μ¬μ©νκΈ°
κΈ°λ³Έ μ¬μ©
// api/src/models/post.model.test.ts
import { bootstrap, test } from "sonamu/test";
import { expect, vi } from "vitest";
import { PostModel } from "./post.model";
import { loadFixtures } from "@/testing/fixture";
bootstrap(vi);
test("μμ±μμ κ²μκΈ μ‘°ν", async () => {
// Fixtures λ‘λ
const { author, authorPost } = await loadFixtures(["author", "authorPost"]);
// ν
μ€νΈ
const postModel = new PostModel();
const { posts } = await postModel.getPostsByAuthor(author.id);
expect(posts).toHaveLength(1);
expect(posts[0].id).toBe(authorPost.id);
});
νμ μμ μ±
test("νμ
μμ ν fixture μ¬μ©", async () => {
// β
IDEκ° fixture μ΄λ¦ μλμμ± μ 곡
const { author, authorPost } = await loadFixtures(["author", "authorPost"]);
// β
νμ
μΆλ‘
author.username; // string
authorPost.title; // string
// β μ‘΄μ¬νμ§ μλ fixture
const { invalid } = await loadFixtures(["invalid"]); // νμ
μλ¬!
});
μ νμ λ‘λ©
test("νμν fixtureλ§ λ‘λ", async () => {
// νμν κ²λ§
const { author } = await loadFixtures(["author"]);
// authorPostλ λ‘λνμ§ μμ β μμ± λΉμ© μ μ½
});
test("μ¬λ¬ fixture λμ λ‘λ", async () => {
// λ³λ ¬λ‘ λ‘λ (Promise.all)
const { author, premiumUser, adminUser } = await loadFixtures([
"author",
"premiumUser",
"adminUser",
]);
expect(author.role).toBe("user");
expect(premiumUser.role).toBe("premium");
expect(adminUser.role).toBe("admin");
});
λ² μ€νΈ νλν°μ€
1. λͺ νν μ΄λ¦
// β
μ¬λ°λ₯Έ λ°©λ²: μ©λκ° λͺ
νν μ΄λ¦
export const loadFixtures = createFixtureLoader({
adminUser: async () => {
/* ... */
},
guestUser: async () => {
/* ... */
},
publishedPost: async () => {
/* ... */
},
draftPost: async () => {
/* ... */
},
premiumSubscription: async () => {
/* ... */
},
});
// β μλͺ»λ λ°©λ²: λΆλͺ
νν μ΄λ¦
export const loadFixtures = createFixtureLoader({
user1: async () => {
/* ... */
},
user2: async () => {
/* ... */
},
post1: async () => {
/* ... */
},
thing: async () => {
/* ... */
},
});
2. μ΅μ λ°μ΄ν°
// β
μ¬λ°λ₯Έ λ°©λ²: νμ νλλ§
export const loadFixtures = createFixtureLoader({
basicUser: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "user",
email: "user@example.com",
password: "password",
// νμ νλλ§
});
return user;
},
});
// β μλͺ»λ λ°©λ²: λΆνμν νλκΉμ§
export const loadFixtures = createFixtureLoader({
complexUser: async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username: "user",
email: "user@example.com",
password: "password",
bio: "Very long bio...",
avatar: "https://...",
preferences: {
/* 볡μ‘ν κ°μ²΄ */
},
// ν
μ€νΈμ νμνμ§ μμ νλλ€
});
return user;
},
});
3. Fixture μ¬μ¬μ©
// β
μ¬λ°λ₯Έ λ°©λ²: λ€λ₯Έ fixtureμμ μ¬μ¬μ©
export const loadFixtures = createFixtureLoader({
user: async () => {
// κΈ°λ³Έ μ¬μ©μ
const userModel = new UserModel();
const { user } = await userModel.create({
username: "user",
email: "user@example.com",
password: "password",
});
return user;
},
userPost: async () => {
// user fixture μ¬μ¬μ©
const { user } = await loadFixtures(["user"]);
const postModel = new PostModel();
const { post } = await postModel.create({
title: "Post",
content: "Content",
author_id: user.id,
});
return post;
},
userPostComment: async () => {
// userPost fixture μ¬μ¬μ© (userλ μλμΌλ‘ ν¬ν¨λ¨)
const { user, userPost } = await loadFixtures(["user", "userPost"]);
const commentModel = new CommentModel();
const { comment } = await commentModel.create({
content: "Comment",
post_id: userPost.id,
author_id: user.id,
});
return comment;
},
});
4. μΌκ΄λ ꡬ쑰
// β
μ¬λ°λ₯Έ λ°©λ²: μΌκ΄λ λ€μ΄λ°κ³Ό ꡬ쑰
export const loadFixtures = createFixtureLoader({
// μ¬μ©μ κ΄λ ¨
adminUser: async () => {
/* ... */
},
guestUser: async () => {
/* ... */
},
premiumUser: async () => {
/* ... */
},
// κ²μκΈ κ΄λ ¨
publishedPost: async () => {
/* ... */
},
draftPost: async () => {
/* ... */
},
// μλ리μ€
blogScenario: async () => {
/* ... */
},
forumScenario: async () => {
/* ... */
},
});
κ³ κΈ ν¨ν΄
ν©ν 리 ν¨μ
// λμ μΌλ‘ fixture μμ±
function createUserFixture(username: string, role: string) {
return async () => {
const userModel = new UserModel();
const { user } = await userModel.create({
username,
email: `${username}@example.com`,
password: "password",
role,
});
return user;
};
}
export const loadFixtures = createFixtureLoader({
adminUser: createUserFixture("admin", "admin"),
guestUser: createUserFixture("guest", "guest"),
moderatorUser: createUserFixture("moderator", "moderator"),
});
μ£Όμμ¬ν
Fixture μμ± μ μ£Όμμ¬ν: 1. Transaction κΈ°λ°: κ° ν
μ€νΈλ§λ€ μλ λ‘€λ°±λ¨ 2. μ΅μ
λ°μ΄ν°: ν
μ€νΈμ νμν μ΅μνμ λ°μ΄ν°λ§ μμ± 3. λ
립μ±: Fixtureλ μλ‘ λ
립μ μ΄μ΄μΌ ν¨ 4.
μ¬μ¬μ©: κ³΅ν΅ fixtureλ₯Ό μ κ·Ή νμ© 5. λͺ
νν μ΄λ¦: fixture μ΄λ¦μ μ©λλ₯Ό λͺ
ννκ² νν
λ€μ λ¨κ³
ν μ€νΈ DB
ν
μ€νΈ DB μ€μ
Fixture λ‘λ©
νλ‘λμ
λ°μ΄ν° κ°μ Έμ€κΈ°
Fixture λκΈ°ν
Fixture β Test DB
ν μ€νΈ ν¬νΌ
createFixtureLoader μμΈ