测试 FactoryGirl 的两个技巧: trait 和 association

xiaoronglv · 2014年10月12日 · 最后由 hiveer 回复于 2014年10月22日 · 6994 次阅读
本帖已被管理员设置为精华贴

我刚刚发现了 FactoryGirl 的两个技巧:trait 和 assocation。真好用呀,让我的测试代码量少了 1/5。 算是后知后觉,大牛们勿笑。

通过两个 case 来说明一下心得体会。

案例 1 创建不同状态的帖子

Before

FactoryGirl.define do
  # 论坛帖子
  factory :post do
    title 'New post'
    body  'I love jesus'
  end

  # 已发布的帖子
  factory :published_post do
    title 'New post'
    body  'I love jesus'
    published_at Date.new(2012, 12, 3)
  end

  # 草稿状态的帖子
  factory :draft_post do
    title 'New post'
    body  'I love jesus'
    published_at nil
  end
end

这样写有很多弊端

  1. 代码重复严重,Post 增加一个属性后,需要更新四个 factory

  2. 每增加一个状态,就需要新创建一个 factory

  3. 太丑了!

Refactor (Good)

我们可以使用继承 inheritance 来减少重复代码。

FactoryGirl.define do
  # 帖子
  factory :post do
    title 'New post'
    body  'I love jesus'

    # 已发布的帖子
    factory :published_post do
      published_at Date.new(2012, 12, 3)
    end

    # 草稿状态的帖子
    factory :draft_post do
      published_at nil
    end
  end
end

重构后,published_post、draft_post 可以从 post 继承 title、body 两个属性,避免了重复代码。

但是还是有缺点

  1. factory (比如:published_post) 的名字越来越长,直逼 Object C 变量的长度。

  2. 条件很多时,使用继承压根不省事儿。

    • draft_post
    • draft_post_with_star
    • draft_post_without_star
    • publish_post
    • publish_post_with_star
    • publish_post_without_star

Refactor (Better)

我们可以使用 Trait 做进一步的优化。Trait 像 Ruby 中可以复用的 module。

FactoryGirl.define do
  # 帖子
  factory :post do
    title 'New post'
    body  'I love jesus'

    # 已发布的帖子
    trait :published do
      published_at Date.new(2012, 12, 3)
    end

    # 草稿状态的帖子
    trait :draft do
      published_at nil
    end

    # 被标记为精华
    trait :star do
      star       true
      star_count 50
    end

    # 未被标记为精华帖子
    trait :without_star do
      star       false
      star_count 2
    end
  end
end

可以按需搭配构造不同 factory

# test/model/post_test.rb
# 创建一个已发布,加精的 post

FactoryGirl.create :post, :published, :star

# 创建一个草稿状态的 post
FactoryGirl.create :post, :draft

案例 2 创建 1:n 关系

我们在写测试的时候,经常要构造 1:n 这种关系的数据,我以前会把相关的逻辑放到测试文件中。

Before

# test/model/post_test.rb
post = create :post
comments = create_list :comments, 3, post_id: post.id
post.reload

遇到这种情况,可以使用 ignored attributes 和 callback 把构造关系的逻辑放到 factory 中。

Refactor

FactoryGirl.define do
  factory :post do
    title 'New post'
    body  'I love jesus'


    # 评论
    trait :with_comments do
      ignore do
        number_of_comments 3
      end

      after :create do |post, evaluator|
        FactoryGirl.create_list :comment, evaluator.number_of_comments, :post => post
      end
    end
  end
end

重构完后,我在 test case 可以使用各种姿势构造数据

# test/model/post_test.rb
# 创建带评论的帖子
FactoryGirl.create :post, :with_comments

# 创建带4个评论的帖子
FactoryGirl.create :post, :with_comments, :number_of_comments => 4

参考资料

FactoryGirl Guide

FactoryGirl Tips and Tricks

很好的分享 :thumbsup: ,正在学 RSpec

:thumbsup: :plus1: `后加个 ruby 就有颜色了~

FactoryGirl.define do
  # 论坛帖子
  factory :post do
    title 'New post'
    body  'I love jesus'
  end

  # 已发布的帖子
  factory :published_post do
    title 'New post'
    body  'I love jesus'
    published_at Date.new(2012, 12, 3)
  end

  # 草稿状态的帖子
  factory :draft_post do
    title 'New post'
    body  'I love jesus'
    published_at nil
  end
end

#5 楼 @zzz6519003

多谢提醒,我加上了。 😄

赞,trait 很方便

好东西,学习了~ 倒数第二块代码里面,

# 评论
trait :without_comments do
  ignore do
    number_of_comments 3
  end

  after :create do |post, evaluator|
    FactoryGirl.create_list :comment, evaluator.number_of_comments, :post => post
  end
end

是不是应该叫:with_comments

直接用个 faker,简单省事

@xiaoronglv 案例一中,我觉得你对于 继承和 trait 的对比有些问题。比如,你对于继承的缺点的描述: 1 名字的长度 2 情况的多样性

对于这样个缺点,在采用 trait 的时候不是一样的存在吗? 我觉得 trait 带来的好处不是消除了这两个缺点,而是带来了更大的灵活性,因为我们可以随意组合一些情况。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号