pnpm fixture ๋ช
๋ น์ด๋ ํ
์คํธ์ ์ฌ์ฉํ ์ผ๊ด๋ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. ๊ฐ๋ฐ ํ๊ฒฝ์ ์ค์ ๋ฐ์ดํฐ๋ฅผ ํ
์คํธ ํ๊ฒฝ์ผ๋ก ๋ณต์ฌํ์ฌ, ์์ ์ ์ด๊ณ ๋ฐ๋ณต ๊ฐ๋ฅํ ํ
์คํธ๋ฅผ ์ํํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ ๊ฐ๋
Fixture๋ ํ
์คํธ์ฉ ๊ณ ์ ๋ ๋ฐ์ดํฐ ์ธํธ์
๋๋ค:
- ์ผ๊ด์ฑ: ๋ชจ๋ ํ
์คํธ๊ฐ ๊ฐ์ ์ด๊ธฐ ๋ฐ์ดํฐ๋ก ์์
- ๊ฒฉ๋ฆฌ: ํ
์คํธ DB์ ๊ฐ๋ฐ DB ๋ถ๋ฆฌ
- ์ฌํ์ฑ: ๊ฐ์ ์กฐ๊ฑด์์ ๋ฐ๋ณต ํ
์คํธ ๊ฐ๋ฅ
- ๊ด๊ณ ๋ณด์กด: ์ธ๋ํค ๊ด๊ณ๊ฐ ์ ์ง๋จ
๋ช
๋ น์ด
init - ํ
์คํธ DB ์ด๊ธฐํ
๊ฐ๋ฐ DB์ ์คํค๋ง๋ฅผ ํ
์คํธ DB๋ก ๋ณต์ฌํฉ๋๋ค.
์คํ ๊ณผ์ :
DUMP...
โ Schema dumped from development_master
SYNC to (REMOTE) Fixture DB...
โ Database myapp_fixture created
โ Schema applied
SYNC to (LOCAL) Testing DB...
โ Database myapp_test created
โ Schema applied
Fixture initialization completed!
์์ฑ๋๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค:
- Fixture DB (์๊ฒฉ): ๊ณต์ fixture ์ ์ฅ์
- Test DB (๋ก์ปฌ): ๋ก์ปฌ ํ
์คํธ์ฉ DB
init์ ์คํค๋ง๋ง ๋ณต์ฌํฉ๋๋ค. ๋ฐ์ดํฐ๋ ํฌํจ๋์ง ์์ต๋๋ค.
import - ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
๊ฐ๋ฐ DB์์ ํน์ ๋ ์ฝ๋๋ฅผ fixture๋ก ์ ์ฅํฉ๋๋ค.
๋ํํ ํ๋กฌํํธ๊ฐ ํ์๋ฉ๋๋ค:
? Please select entity: (Use arrow keys)
โฏ User
Post
Comment
? Enter record IDs (comma-separated): 1,2,3
Importing fixtures...
โ Imported User #1
โ Imported User #2
โ Imported User #3
โ Imported related records (5 dependencies)
Fixture import completed!
์๋์ผ๋ก ํฌํจ๋๋ ๊ด๋ จ ๋ฐ์ดํฐ:
- ์ธ๋ํค๋ก ์ฐธ์กฐ๋๋ ๋ ์ฝ๋
- ๊ด๊ณ ํ
์ด๋ธ์ ๋ ์ฝ๋
- ์ญ์ฐธ์กฐ ๋ ์ฝ๋ (์ ํ ๊ฐ๋ฅ)
sync - ๋๊ธฐํ
์ ์ฅ๋ fixture๋ฅผ ํ
์คํธ DB์ ์ ์ฉํฉ๋๋ค.
์คํ ๊ณผ์ :
Syncing fixtures...
Clearing test database...
โ Cleared all tables
Applying fixtures...
โ Applied 3 users
โ Applied 7 posts
โ Applied 12 comments
โ Applied 5 categories
Fixture sync completed!
ํ
์คํธ ์คํ ์ ์ sync๋ฅผ ํธ์ถํ๋ฉด ํญ์ ๊นจ๋ํ ์ํ์์ ์์ํ ์ ์์ต๋๋ค.
์ฌ์ฉ ์ํฌํ๋ก์ฐ
1. ์ด๊ธฐ ์ค์
ํ๋ก์ ํธ ์์ ์ ํ ๋ฒ๋ง ์คํํฉ๋๋ค.
# ํ
์คํธ DB ์์ฑ ๋ฐ ์คํค๋ง ๋ณต์ฌ
pnpm fixture init
2. ํ์ํ ๋ฐ์ดํฐ ์ ํ
ํ
์คํธ์ ํ์ํ ์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
# User #1, #2, #3 ๊ฐ์ ธ์ค๊ธฐ
pnpm fixture import
# Entity: User
# IDs: 1,2,3
3. ํ
์คํธ ์์ฑ
import { FixtureManager } from "sonamu/test";
beforeAll(async () => {
// Fixture ๋๊ธฐํ
await FixtureManager.sync();
});
test("User๋ฅผ ์ด๋ฉ์ผ๋ก ์ฐพ๊ธฐ", async () => {
// Fixture์ User #1 ์ฌ์ฉ
const user = await UserModel.findByEmail("[email protected]");
expect(user).toBeDefined();
expect(user.id).toBe(1);
});
4. ํ
์คํธ ์คํ
ํ
์คํธ๊ฐ ์คํ๋ ๋๋ง๋ค fixture๊ฐ ์๋์ผ๋ก ๋๊ธฐํ๋ฉ๋๋ค.
Fixture ํ์ผ
์ ์ฅ ์์น
๐src/
๐fixtures/
๐JSONusers.json - User fixture
๐JSONposts.json - Post fixture
๐JSONcomments.json - Comment fixture
ํ์ผ ํ์
[
{
"id": 1,
"email": "[email protected]",
"name": "ํ๊ธธ๋",
"created_at": "2024-01-15T00:00:00.000Z"
},
{
"id": 2,
"email": "[email protected]",
"name": "๊น์ฒ ์",
"created_at": "2024-01-16T00:00:00.000Z"
}
]
ํน์ง:
- JSON ํ์
- Entity๋ณ๋ก ํ์ผ ๋ถ๋ฆฌ
- ๊ด๊ณ ๋ฐ์ดํฐ ์๋ ํฌํจ
- Git์ผ๋ก ๋ฒ์ ๊ด๋ฆฌ
๊ด๊ณ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
์ธ๋ํค ์๋ ํฌํจ
Post๋ฅผ importํ๋ฉด ์ฐ๊ฒฐ๋ User๋ ์๋์ผ๋ก ํฌํจ๋ฉ๋๋ค.
pnpm fixture import
# Entity: Post
# IDs: 1
# ์๋์ผ๋ก ํฌํจ๋จ:
# โ Post #1
# โ User #5 (author)
# โ Category #2 (category)
N:M ๊ด๊ณ ์ฒ๋ฆฌ
๋ค๋๋ค ๊ด๊ณ ํ
์ด๋ธ๋ ์๋์ผ๋ก ํฌํจ๋ฉ๋๋ค.
pnpm fixture import
# Entity: Post
# IDs: 1
# ์๋์ผ๋ก ํฌํจ๋จ:
# โ Post #1
# โ post_tags (์ค๊ฐ ํ
์ด๋ธ)
# โ Tag #1, #2, #3 (์ฐ๊ฒฐ๋ ํ๊ทธ๋ค)
ํ
์คํธ ๊ฒฉ๋ฆฌ
๊ฐ ํ
์คํธ๋ง๋ค ์ด๊ธฐํ
describe("User CRUD", () => {
beforeEach(async () => {
// ๊ฐ ํ
์คํธ ์ ์ fixture ์ฌ์ ์ฉ
await FixtureManager.sync();
});
test("์ฌ์ฉ์ ์์ฑ", async () => {
const user = await UserModel.create({
email: "[email protected]",
name: "์ ๊ท์ฌ์ฉ์",
});
expect(user.id).toBeDefined();
});
test("์ฌ์ฉ์ ์ญ์ ", async () => {
await UserModel.deleteById(1);
const user = await UserModel.findById(1);
expect(user).toBeNull();
});
});
๊ฒฉ๋ฆฌ ํจ๊ณผ:
- ๊ฐ ํ
์คํธ๊ฐ ๋
๋ฆฝ์
- ํ
์คํธ ์์ ๋ฌด๊ด
- ๋ณ๋ ฌ ์คํ ๊ฐ๋ฅ
ํธ๋์ญ์
๊ฒฉ๋ฆฌ
import { Sonamu } from "sonamu";
test("ํธ๋์ญ์
ํ
์คํธ", async () => {
await Sonamu.runScript(async () => {
// ํธ๋์ญ์
๋ด์์ ์คํ
const user = await UserModel.create({...});
const post = await PostModel.create({...});
// ํ
์คํธ ์๋ฃ ํ ์๋ ๋กค๋ฐฑ
});
});
์ค์ ์์
๋ณต์กํ ๊ด๊ณ ํ
์คํธ
describe("Post with Comments", () => {
beforeAll(async () => {
// Post #1 (+ User #1, Comments)
await FixtureManager.sync();
});
test("๋๊ธ์ด ์๋ Post ์กฐํ", async () => {
const post = await PostModel.findById(1, {
include: ["comments"],
});
expect(post.comments).toHaveLength(3);
expect(post.comments[0].author_id).toBe(2);
});
});
ํน์ ์๋๋ฆฌ์ค ๋ฐ์ดํฐ
describe("Premium User Features", () => {
beforeAll(async () => {
// Premium user๋ง import
await FixtureManager.importFixture("User", [10, 11, 12]);
await FixtureManager.sync();
});
test("ํ๋ฆฌ๋ฏธ์ ๊ธฐ๋ฅ ์ ๊ทผ", async () => {
const user = await UserModel.findById(10);
expect(user.isPremium).toBe(true);
expect(await user.canAccessFeature("advanced")).toBe(true);
});
});
๋ฌธ์ ํด๊ฒฐ
Fixture DB ์ฐ๊ฒฐ ์คํจ
๋ฌธ์ : ์๊ฒฉ fixture DB์ ์ ๊ทผ ๋ถ๊ฐ
Error: connect ECONNREFUSED
ํด๊ฒฐ:
export default {
database: {
fixture: {
client: "pg",
connection: {
host: "fixture-db.example.com", // ์ฌ๋ฐ๋ฅธ ํธ์คํธ
database: "myapp_fixture",
user: "postgres",
password: process.env.FIXTURE_DB_PASSWORD,
},
},
},
};
๊ด๊ณ ๋ฐ์ดํฐ ๋๋ฝ
๋ฌธ์ : ์ธ๋ํค ์๋ฌ ๋ฐ์
Error: Foreign key constraint violation
ํด๊ฒฐ:
# ๊ด๋ จ ๋ฐ์ดํฐ๋ฅผ ๋จผ์ import
pnpm fixture import
# Entity: User
# IDs: 1,2,3
pnpm fixture import
# Entity: Post
# IDs: 1,2,3 # User 1,2,3์ ์ฐธ์กฐ
Fixture ์ถฉ๋
๋ฌธ์ : ID ์ค๋ณต
Error: Duplicate entry '1' for key 'PRIMARY'
ํด๊ฒฐ:
# Test DB ์ด๊ธฐํ
pnpm fixture init
# Fixture ์ฌ๋๊ธฐํ
pnpm fixture sync
๋ฒ ์คํธ ํ๋ํฐ์ค
1. ์ต์ํ์ ๋ฐ์ดํฐ
# โ ๋ชจ๋ ๋ฐ์ดํฐ import
pnpm fixture import
# IDs: 1-1000
# โ
ํ์ํ ๋ฐ์ดํฐ๋ง
pnpm fixture import
# IDs: 1,2,3 # ๋ํ ์ผ์ด์ค๋ง
2. ์๋ฏธ์๋ ๋ฐ์ดํฐ
// โ
ํ
์คํธ ๋ชฉ์ ์ด ๋ช
ํํ ๋ฐ์ดํฐ
{
"id": 1,
"email": "[email protected]",
"role": "admin",
"name": "๊ด๋ฆฌ์"
}
{
"id": 2,
"email": "[email protected]",
"role": "user",
"name": "์ผ๋ฐ์ฌ์ฉ์"
}
3. ๋ฒ์ ๊ด๋ฆฌ
# Fixture ํ์ผ์ Git์ ํฌํจ
git add src/fixtures/
git commit -m "Update test fixtures"
4. CI/CD ํตํฉ
.github/workflows/test.yml
jobs:
test:
steps:
- name: Setup Database
run: |
pnpm fixture init
pnpm fixture sync
- name: Run Tests
run: pnpm test
๋ค์ ๋จ๊ณ