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๋ฅผ ํธ์ถํ๋ฉด ํญ์ ๊นจ๋ํ ์ํ์์ ์์ํ ์ ์์ต๋๋ค.
gen - ์๋ Fixture ์์ฑ
Entity์ cone ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ์ฌ ์๋์ผ๋ก Fixture๋ฅผ ์์ฑ ํฉ๋๋ค. ์ค์ ๋ฐ์ดํฐ๊ฐ ์๊ฑฐ๋ ๊ฐ์์ ํ
์คํธ ๋ฐ์ดํฐ๊ฐ ํ์ํ ๋ ์ ์ฉํฉ๋๋ค.
๋ํํ ๋ชจ๋ :
? Fixture๋ฅผ ์์ฑํ Entity๋ฅผ ์ ํํ์ธ์: (Use arrow keys)
โฏ โฏ User
โฏ Post
โฏ Comment
? ๊ฐ Entity๋ณ ์์ฑ ๊ฐ์: 5
? User ์ํฐํฐ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ์์ฑ ๋ฐฉ์์ ์ ํํ์ธ์:
โฏ 1. ๋ก๊ทธ์ธ ๊ฐ๋ฅํ ์ฌ์ฉ์ fixture ์์ฑ
2. ํ์ธ์ฉ ๋ฐ์ดํฐ(๋ก๊ทธ์ธ ๋ถ๊ฐ) ์์ฑ
? ์ ์ฅ ๋ฐฉ์: (Use arrow keys)
โฏ Fixture DB์ ์ ์ฅ
ํ์ผ๋ก ์ ์ฅ (์๋ ํ์ผ๋ช
)
ํ์ผ๋ก ์ ์ฅ (ํ์ผ๋ช
์ง์ )
์ ์ฅ ์ ํจ (์ถ๋ ฅ๋ง)
? LLM์ผ๋ก ๋ ํ์ค์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ๊น์? (fixtureHint ๊ธฐ๋ฐ, ANTHROPIC_API_KEY ํ์) No
User ์ํฐํฐ๊ฐ ์ ํ์ ํฌํจ๋ ๊ฒฝ์ฐ์๋ง ์์ฑ ๋ฐฉ์ ์ ํ ํ๋กฌํํธ๊ฐ ํ์๋ฉ๋๋ค.
User ์ํฐํฐ๊ฐ ์์ผ๋ฉด ๋ฐ๋ก ์ ์ฅ ๋ฐฉ์ ์ ํ์ผ๋ก ๋์ด๊ฐ๋๋ค.
๋ก๊ทธ์ธ ๊ฐ๋ฅํ User Fixture ์์ฑ :
User ์ํฐํฐ๋ฅผ ์ ํํ๊ณ โ๋ก๊ทธ์ธ ๊ฐ๋ฅํ ์ฌ์ฉ์ fixture ์์ฑโ์ ์ ํํ๋ฉด, better-auth์ sign-up/email ์๋ํฌ์ธํธ๋ฅผ ํตํด ์ค์ ๋ก ๋ก๊ทธ์ธ ๊ฐ๋ฅํ ์ฌ์ฉ์๋ฅผ ์์ฑํฉ๋๋ค.
์ด ๋ชจ๋์ ๋์:
FixtureGenerator๋ก User ๋ฐ์ดํฐ(name, email, username ๋ฑ)๋ฅผ ์์ฑ
Sonamu.auth.handler()๋ฅผ ํตํด better-auth sign-up API๋ฅผ ํธ์ถํ์ฌ ์ฌ์ฉ์ ๋ฑ๋ก
์์ฑ๋ ๋ชจ๋ ์ฌ์ฉ์์ email_verified๋ฅผ true๋ก ์๋ ์
๋ฐ์ดํธ
์์ฑ๋ ๊ณ์ ๋ชฉ๋ก(email, password)์ ์ฝ์์ ์ถ๋ ฅ
๋ก๊ทธ์ธ ๊ฐ๋ฅํ ์ฌ์ฉ์ 5๋ช
์์ฑ ์ค...
โ
alice@example.com ์์ฑ ์๋ฃ
โ
bob@example.com ์์ฑ ์๋ฃ
โ ๏ธ carol@example.com ์ด๋ฏธ ์กด์ฌ - ๊ฑด๋๋๋๋ค.
โ
dave@example.com ์์ฑ ์๋ฃ
โ
eve@example.com ์์ฑ ์๋ฃ
email_verified = true ์
๋ฐ์ดํธ ์๋ฃ
โ
4๋ช
์ ๋ก๊ทธ์ธ ๊ฐ๋ฅํ ์ฌ์ฉ์ ์์ฑ ์๋ฃ
์์ฑ๋ ๊ณ์ ๋ชฉ๋ก:
โโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ (index) โ email โ password โ
โโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโค
โ 0 โ alice@example.com โ Test1234! โ
โ 1 โ bob@example.com โ Test1234! โ
โ 2 โ dave@example.com โ Test1234! โ
โ 3 โ eve@example.com โ Test1234! โ
โโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโ
์์ฑ๋ ๋ชจ๋ ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ๋ Test1234!๋ก ๋์ผํฉ๋๋ค. ์ด ๊ธฐ๋ฅ์ ๊ฐ๋ฐ/ํ
์คํธ ํ๊ฒฝ ์ ์ฉ์ด๋ฉฐ,
ํ๋ก๋์
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฌ์ฉํด์๋ ์ ๋ฉ๋๋ค.
โํ์ธ์ฉ ๋ฐ์ดํฐ(๋ก๊ทธ์ธ ๋ถ๊ฐ) ์์ฑโ์ ์ ํํ๋ฉด ๊ธฐ์กด๊ณผ ๋์ผํ๊ฒ generateBatch()๋ก DB์ ์ง์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ better-auth ์ธ์ฆ ๋ ์ฝ๋๊ฐ ์์ฑ๋์ง ์์ผ๋ฏ๋ก ๋ก๊ทธ์ธ์ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
CLI ์ต์
:
์ต์
์ค๋ช
์์ --all๋ชจ๋ Entity์ ๋ํด ์์ฑ pnpm fixture gen --all--includeํน์ Entity๋ง ํฌํจ pnpm fixture gen --include User,Post--excludeํน์ Entity ์ ์ธ (โall๊ณผ ํจ๊ป ์ฌ์ฉ) pnpm fixture gen --all --exclude Comment--count์์ฑํ ๋ ์ฝ๋ ์ pnpm fixture gen --include User --count 10--save-to์ ์ฅ ๋ฐฉ์ ์ง์ pnpm fixture gen --save-to db--use-llmLLM์ผ๋ก ํ์ค์ ์ธ ๋ฐ์ดํฐ ์์ฑ (fixtureHint ๊ธฐ๋ฐ, ANTHROPIC_API_KEY ํ์) pnpm fixture gen --use-llm--no-cacheLLM ์บ์ ๋นํ์ฑํ (--use-llm๊ณผ ํจ๊ป ์ฌ์ฉ) pnpm fixture gen --use-llm --no-cache
์ ์ฅ ๋ฐฉ์ :
db: Fixture DB์ ์ง์ ์ ์ฅ (๊ธฐ๋ณธ๊ฐ)
file: test/fixtures/{entity_table}.json ํ์ผ๋ก ์ ์ฅ
file:{filename}: ์ง์ ๋ ํ์ผ๋ช
์ผ๋ก ์ ์ฅ
none: ์ ์ฅํ์ง ์๊ณ ์ฝ์์ ์ถ๋ ฅ๋ง
# ์์: User, Post ๊ฐ 10๊ฐ์ฉ ์์ฑํ์ฌ DB์ ์ ์ฅ
pnpm fixture gen --include User,Post --count 10 --save-to db
๊ฐ ์์ฑ ์ฐ์ ์์ :
๊ฐ ํ๋์ ๊ฐ์ ์์ฑํ ๋ ์๋ ์์๋๋ก ์ ์ฉ๋ฉ๋๋ค. ๋จผ์ ๋งค์นญ๋๋ ์ ๋ต์ด ์ฌ์ฉ๋๊ณ , ์ดํ ๋จ๊ณ๋ ๊ฑด๋๋๋๋ค.
Relation โ ์ธ๋ํค ๊ด๊ณ๊ฐ ์๋ ํ๋๋ ์๋์ผ๋ก ๊ด๋ จ ๋ ์ฝ๋๋ฅผ ์์ฑ/์ฐธ์กฐํฉ๋๋ค.
cone.note + LLM โ --use-llm ์ต์
์ด ์ผ์ ธ ์๊ณ , ํด๋น ํ๋์ cone์ note๊ฐ ์ง์ ๋ ๊ฒฝ์ฐ LLM์ด note ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ํ์ค์ ์ธ ๊ฐ์ ์์ฑํฉ๋๋ค. LLM ํธ์ถ์ด ์คํจํ๋ฉด ๋ค์ ๋จ๊ณ๋ก ํด๋ฐฑํฉ๋๋ค.
cone.fixtureGenerator โ cone์ fixtureGenerator(Faker ํํ์ ๋ฑ)๊ฐ ์ง์ ๋ ๊ฒฝ์ฐ ์ด๋ฅผ ์คํํฉ๋๋ค.
cone.fixtureDefault โ cone์ fixtureDefault ๊ณ ์ ๊ฐ์ด ์ง์ ๋ ๊ฒฝ์ฐ ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ์
๋ณ ๊ธฐ๋ณธ ์์ฑ โ ์ ์ด๋ค ์ ๋ต๋ ํด๋นํ์ง ์์ผ๋ฉด ํ๋ ํ์
(string, number, date ๋ฑ)์ ๋ฐ๋ผ ์๋ ์์ฑํฉ๋๋ค.
--use-llm ์ฌ์ฉ ์ cone.note๊ฐ ์๋ ํ๋๋ fixtureGenerator๋ณด๋ค LLM์ด ์ฐ์ ํฉ๋๋ค. ์ด๋ฅผ ํตํด
๋๋ฉ์ธ์ ๋ง๋ ๋ ํ์ค์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
LLM์ด ์์ฑํ ๋ฌธ์์ด์ด ํด๋น ํ๋์ length ์ ์ฝ์ ์ด๊ณผํ๋ฉด ์๋์ผ๋ก ์๋ผ๋
๋๋ค. ์๋ฅผ ๋ค์ด
varchar(100) ํ๋์ ๋ํด LLM์ด 120์๋ฅผ ์์ฑํ๋ฉด ์ 100์๋ง ์ฌ์ฉ๋ฉ๋๋ค.
Companion Fixture ์๋ ์์ฑ :
Entity์ cone์ fixtureCompanions๊ฐ ์ ์ธ๋์ด ์์ผ๋ฉด, ๋ถ๋ชจ fixture ์์ฑ ์ companion Entity์ fixture๋ ํจ๊ป ์๋ ์์ฑ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด better-auth ๊ธฐ๋ฐ ํ๋ก์ ํธ์์ User fixture๋ฅผ ์์ฑํ๋ฉด credentials Account๋ ์๋์ผ๋ก ์์ฑ๋ฉ๋๋ค.
fixtureCompanions๋ Entity์ id prop cone์ ์ ์ธํฉ๋๋ค:
{
"name" : "id" ,
"type" : "string" ,
"cone" : {
"fixtureStrategy" : "sequence" ,
"fixtureCompanions" : [
{
"entity" : "Account" ,
"overrides" : {
"provider_id" : "credential" ,
"account_id" : "{{email}}" ,
"password" : "{{email}}"
}
}
]
}
}
์์ฑ ์ค๋ช
entityํจ๊ป ์์ฑํ companion Entity ์ด๋ฆ overridescompanion fixture์ ์ ์ฉํ ๊ณ ์ ์ค๋ฒ๋ผ์ด๋ ๊ฐ. {{fieldName}} ํ์์ผ๋ก ๋ถ๋ชจ fixture์ ํ๋ ๊ฐ์ ์ฐธ์กฐํ ์ ์์ต๋๋ค count๋ถ๋ชจ 1๊ฐ๋น ์์ฑํ companion ๊ฐ์ (๊ธฐ๋ณธ๊ฐ: 1)
better-auth๋ฅผ ์ฌ์ฉํ๋ ๊ธฐ์กด ํ๋ก์ ํธ์ fixtureCompanions๋ฅผ ์๊ธ ์ ์ฉํ๋ ค๋ฉด:
pnpm sonamu auth add-companions
์ด ๋ช
๋ น์ด๋ better-auth Entity(User ๋ฑ)์ entity.json์ fixtureCompanions ์ค์ ์ด ์๋ ๊ฒฝ์ฐ์๋ง ์๋์ผ๋ก ์ถ๊ฐํฉ๋๋ค. ์ด๋ฏธ ์ค์ ์ด ์๋ Entity๋ ๊ฑด๋๋๋๋ค.
parentId ์ํฐํฐ Fixture ์์ฑ :
parentId๊ฐ ์ง์ ๋ ์ํฐํฐ(์์ ๊ด๊ณ์ ์๋ธํ์
)๋ gen ์ ํน๋ณํ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค. ์๋ก์ด ๋ถ๋ชจ ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ , DB์์ ์์ง ์๋ธํ์
ํ์ด ์๋ ๊ธฐ์กด ๋ถ๋ชจ ๋ ์ฝ๋๋ฅผ ์ฐพ์ ํด๋น ID๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์๋ฅผ ๋ค์ด Paper๊ฐ Achievement์ ์๋ธํ์
(parentId: "Achievement")์ธ ๊ฒฝ์ฐ:
achievements ํ
์ด๋ธ์์ ์์ง papers ํ
์ด๋ธ์ ๋์ํ๋ ํ์ด ์๋ ID๋ฅผ ์กฐํ
ํด๋น ID๋ก Paper fixture๋ฅผ ์์ฑํ์ฌ ๋ถ๋ชจ-์์ ๊ด๊ณ๋ฅผ ํ์ฑ
๋ถ๋ชจ ์กฐํ ์ ์กฐ๊ฑด์ ์ถ๊ฐํ๋ ค๋ฉด id prop์ cone์ fixtureParentOverrides๋ฅผ ์ง์ ํฉ๋๋ค:
{
"name" : "id" ,
"type" : "integer" ,
"cone" : {
"fixtureParentOverrides" : {
"achievement_type" : "PAPER"
}
}
}
์์ฑ ์ค๋ช
fixtureParentOverrides๋ถ๋ชจ ํ
์ด๋ธ์์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ ์ฝ๋๋ฅผ ์กฐํํ ๋ ์ ์ฉํ WHERE ์กฐ๊ฑด. ์๋ฅผ ๋ค์ด ์๋ธํ์
๋ณ๋ก ๊ตฌ๋ถ ์ปฌ๋ผ์ด ์๋ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ผ๋ก ํํฐ๋งํฉ๋๋ค
๋ถ๋ชจ ํ
์ด๋ธ์ ์๋ธํ์
์ด ์๋ ๋ ์ฝ๋๊ฐ ๋ถ์กฑํ๋ฉด ํด๋น ์ํฐํฐ์ fixture ์์ฑ์ ๊ฑด๋๋๋๋ค. ์ฌ์ ์
๋ถ๋ชจ ์ํฐํฐ์ fixture๋ฅผ ์ถฉ๋ถํ ์์ฑํด ๋์ด์ผ ํฉ๋๋ค.
fetch - ์ด์ DB์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
์ค์ ์ด์ DB(๋๋ ๊ฐ๋ฐ DB)์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ Fixture DB์ ์ ์ฅํฉ๋๋ค. ํ์ค์ ์ธ ํ
์คํธ ๋ฐ์ดํฐ๊ฐ ํ์ํ ๋ ์ฌ์ฉํฉ๋๋ค.
๋ํํ ๋ชจ๋ :
? Importํ Entity๋ฅผ ์ ํํ์ธ์: (Use arrow keys)
โฏ โฏ User
โฏ Post
โฏ Comment
User, Post import ์ค...
โ User: 10๊ฐ import ์๋ฃ
โ Post: 10๊ฐ import ์๋ฃ
CLI ์ต์
:
์ต์
์ค๋ช
์์ --all๋ชจ๋ Entity์์ ๊ฐ์ ธ์ค๊ธฐ pnpm fixture fetch --all--includeํน์ Entity๋ง ํฌํจ pnpm fixture fetch --include User,Post--excludeํน์ Entity ์ ์ธ pnpm fixture fetch --all --exclude Log--strategy๋ฐ์ดํฐ ์ ํ ์ ๋ต pnpm fixture fetch --strategy recent--limit๊ฐ์ ธ์ฌ ๋ ์ฝ๋ ์ pnpm fixture fetch --limit 20
๋ฐ์ดํฐ ์ ํ ์ ๋ต :
recent (๊ธฐ๋ณธ๊ฐ): ์ต๊ทผ ๋ฐ์ดํฐ๋ถํฐ ๊ฐ์ ธ์ด
sample: ๋ฌด์์ ์ํ๋ง
# ์์: User์ ์ต๊ทผ 20๊ฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
pnpm fixture fetch --include User --strategy recent --limit 20
ํน์ง :
๊ด๊ณ๋ ๋ฐ์ดํฐ๋ ํจ๊ป ์๋์ผ๋ก ๊ฐ์ ธ์ด (๊น์ด 2 ๋ ๋ฒจ๊น์ง)
์ธ๋ํค ์ฐธ์กฐ ๋ฌด๊ฒฐ์ฑ ์ ์ง
explore - DB ๋ฐ์ดํฐ ์กฐํ
DB์ ๋ฐ์ดํฐ๋ฅผ ์กฐํ๋ง ํฉ๋๋ค. ์ ์ฅํ์ง ์๊ณ ๋น ๋ฅด๊ฒ ๋ฐ์ดํฐ๋ฅผ ํ์ธํ ๋ ์ ์ฉํฉ๋๋ค.
๋ํํ ๋ชจ๋ :
? ํ์ํ Entity: User
User 10๊ฐ ์กฐํ ์๋ฃ:
โโโโโโโโโโโฌโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโ
โ (index) โ id โ email โ name โ
โโโโโโโโโโโผโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโค
โ 0 โ 1 โ 'user1@example.com'โ 'ํ๊ธธ๋' โ
โ 1 โ 2 โ 'user2@example.com'โ '๊น์ฒ ์' โ
โ ... โ... โ ... โ ... โ
โโโโโโโโโโโดโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโ
CLI ์ต์
:
์ต์
์ค๋ช
์์ --includeํ์ํ Entity pnpm fixture explore --include User--strategy๋ฐ์ดํฐ ์ ํ ์ ๋ต pnpm fixture explore --strategy sample--limit์กฐํํ ๋ ์ฝ๋ ์ pnpm fixture explore --limit 5
๋ฐ์ดํฐ ์ ํ ์ ๋ต :
sample (๊ธฐ๋ณธ๊ฐ): ๋ฌด์์ ์ํ
recent: ์ต๊ทผ ๋ฐ์ดํฐ
random: ์์ ๋ฌด์์
ids: ํน์ ID ์ง์
query: ์ปค์คํ
์ฟผ๋ฆฌ
# ์์: User ํ
์ด๋ธ์์ ์ต๊ทผ 5๊ฐ ์กฐํ
pnpm fixture explore --include User --strategy recent --limit 5
์ฌ์ฉ ์ํฌํ๋ก์ฐ
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 ( "user1@example.com" );
expect ( user ). toBeDefined ();
expect ( user . id ). toBe ( 1 );
});
4. ํ
์คํธ ์คํ
ํ
์คํธ๊ฐ ์คํ๋ ๋๋ง๋ค fixture๊ฐ ์๋์ผ๋ก ๋๊ธฐํ๋ฉ๋๋ค.
Fixture ํ์ผ
์ ์ฅ ์์น
ํ์ผ ํ์
[
{
"id" : 1 ,
"email" : "user1@example.com" ,
"name" : "ํ๊ธธ๋" ,
"created_at" : "2024-01-15T00:00:00.000Z"
},
{
"id" : 2 ,
"email" : "user2@example.com" ,
"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: "new@example.com" ,
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" : "admin@example.com" ,
"role" : "admin" ,
"name" : "๊ด๋ฆฌ์"
}
{
"id" : 2 ,
"email" : "user@example.com" ,
"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
๋ค์ ๋จ๊ณ
Testing Naite ํ
์คํธ ํ๋ ์์ํฌ ์์๋ณด๊ธฐ
migrate ๋ง์ด๊ทธ๋ ์ด์
๊ด๋ฆฌํ๊ธฐ