Factories
Bob のファクトリー機能は、テスト用のモックデータを簡単に作成するためのツールです。
基本的なファクトリー
データベーステーブルから自動的にファクトリーが生成されます。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
age INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
生成されるファクトリー:
// UserFactory はテスト用のユーザーを作成するファクトリー
type UserFactory struct {
Name string
Email string
Age *int
CreatedAt *time.Time
}
// NewUserFactory は新しいファクトリーを作成
func NewUserFactory() *UserFactory {
return &UserFactory{
Name: "Test User",
Email: "test@example.com",
Age: nil,
CreatedAt: nil,
}
}
// Build はUserSetterを構築
func (f *UserFactory) Build() UserSetter {
return UserSetter{
Name: omit.From(f.Name),
Email: omit.From(f.Email),
Age: omit.FromPtr(f.Age),
CreatedAt: omit.FromPtr(f.CreatedAt),
}
}
// Create はデータベースにユーザーを作成
func (f *UserFactory) Create(ctx context.Context, db bob.Executor) (*User, error) {
setter := f.Build()
return Users.Insert(ctx, db, setter)
}
ファクトリーの使用方法
基本的な使用
func TestUserCreation(t *testing.T) {
// デフォルト値でユーザーを作成
user, err := NewUserFactory().Create(ctx, db)
assert.NoError(t, err)
assert.Equal(t, "Test User", user.Name)
// カスタム値でユーザーを作成
user, err = NewUserFactory().
WithName("John Doe").
WithEmail("john@example.com").
WithAge(30).
Create(ctx, db)
assert.NoError(t, err)
assert.Equal(t, "John Doe", user.Name)
}
メソッドチェーン
// WithXXX メソッドが自動生成される
func (f *UserFactory) WithName(name string) *UserFactory {
f.Name = name
return f
}
func (f *UserFactory) WithEmail(email string) *UserFactory {
f.Email = email
return f
}
func (f *UserFactory) WithAge(age int) *UserFactory {
f.Age = &age
return f
}
func (f *UserFactory) WithCreatedAt(createdAt time.Time) *UserFactory {
f.CreatedAt = &createdAt
return f
}
複数のオブジェクト作成
func TestMultipleUsers(t *testing.T) {
// 複数のユーザーを作成
users := make([]*User, 5)
for i := 0; i < 5; i++ {
user, err := NewUserFactory().
WithName(fmt.Sprintf("User %d", i+1)).
WithEmail(fmt.Sprintf("user%d@example.com", i+1)).
Create(ctx, db)
assert.NoError(t, err)
users[i] = user
}
assert.Len(t, users, 5)
}
リレーションシップ付きファクトリー
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
title VARCHAR(200) NOT NULL,
content TEXT,
published BOOLEAN DEFAULT FALSE
);
生成されるファクトリー:
type PostFactory struct {
UserID int
Title string
Content *string
Published *bool
}
func NewPostFactory() *PostFactory {
return &PostFactory{
UserID: 1, // デフォルト値
Title: "Test Post",
Content: nil,
Published: nil,
}
}
// リレーションシップを設定するメソッド
func (f *PostFactory) ForUser(user *User) *PostFactory {
f.UserID = user.ID
return f
}
func (f *PostFactory) WithTitle(title string) *PostFactory {
f.Title = title
return f
}
func (f *PostFactory) WithContent(content string) *PostFactory {
f.Content = &content
return f
}
func (f *PostFactory) Published() *PostFactory {
published := true
f.Published = &published
return f
}
リレーションシップの使用
func TestPostWithUser(t *testing.T) {
// まずユーザーを作成
user, err := NewUserFactory().
WithName("John Doe").
Create(ctx, db)
assert.NoError(t, err)
// そのユーザーに関連する投稿を作成
post, err := NewPostFactory().
ForUser(user).
WithTitle("My First Post").
WithContent("This is my first post").
Published().
Create(ctx, db)
assert.NoError(t, err)
assert.Equal(t, user.ID, post.UserID)
assert.Equal(t, "My First Post", post.Title)
assert.True(t, *post.Published)
}
カスタムファクトリーメソッド
// カスタムファクトリーメソッドの例
func NewUserFactoryWithRandomEmail() *UserFactory {
return NewUserFactory().
WithEmail(fmt.Sprintf("user%d@example.com", rand.Intn(10000)))
}
func NewPublishedPostFactory() *PostFactory {
return NewPostFactory().
WithTitle("Published Post").
WithContent("This is a published post").
Published()
}
func NewUserWithPosts(postCount int) (*User, []*Post, error) {
user, err := NewUserFactory().Create(ctx, db)
if err != nil {
return nil, nil, err
}
posts := make([]*Post, postCount)
for i := 0; i < postCount; i++ {
post, err := NewPostFactory().
ForUser(user).
WithTitle(fmt.Sprintf("Post %d", i+1)).
Create(ctx, db)
if err != nil {
return nil, nil, err
}
posts[i] = post
}
return user, posts, nil
}
設定オプション
factories:
enabled: true
package: "factories"
output: "./testdata/factories"
defaults:
string_length: 10
integer_range: [1, 100]
boolean_default: false
templates:
- name: "user_with_posts"
factory: "user"
relations:
- name: "posts"
count: 3
テストでの使用例
func TestUserService(t *testing.T) {
// テストデータのセットアップ
user, err := NewUserFactory().
WithName("Test User").
WithEmail("test@example.com").
WithAge(25).
Create(ctx, db)
require.NoError(t, err)
// 投稿を作成
_, err = NewPostFactory().
ForUser(user).
WithTitle("Test Post").
Published().
Create(ctx, db)
require.NoError(t, err)
// サービスをテスト
service := NewUserService(db)
publishedPosts, err := service.GetPublishedPosts(user.ID)
require.NoError(t, err)
assert.Len(t, publishedPosts, 1)
assert.Equal(t, "Test Post", publishedPosts[0].Title)
}
ランダムデータ生成
func NewRandomUserFactory() *UserFactory {
return NewUserFactory().
WithName(RandomName()).
WithEmail(RandomEmail()).
WithAge(RandomAge())
}
func RandomName() string {
names := []string{"John", "Jane", "Bob", "Alice", "Charlie"}
return names[rand.Intn(len(names))]
}
func RandomEmail() string {
domains := []string{"example.com", "test.org", "demo.net"}
return fmt.Sprintf("user%d@%s", rand.Intn(1000), domains[rand.Intn(len(domains))])
}
func RandomAge() int {
return rand.Intn(50) + 18 // 18-67歳
}
Bob のファクトリー機能により、テストデータの作成が大幅に簡素化され、一貫性のあるテストが書けるようになります。