has_many:运行rspec

时间:2016-04-04 21:31:43

标签: ruby-on-rails rspec

我正在测试我的has_many :through - 与rspec的自我双向关系,我的联合记录只是从一个案例消失到另一个案例。当然,我毕竟不是在每个之间清理数据库。

author.rb

class Author < ActiveRecord::Base

  has_many :follower_relationships,
    foreign_key: :follower_id,
    class_name: AuthorsRelationship,
    dependent: :destroy
  has_many :followed_relationships,
    foreign_key: :followed_id,
    class_name: AuthorsRelationship,
    dependent: :destroy
  has_many :followers, through: :followed_relationships
  has_many :followeds, through: :follower_relationships

  def follow(followed)
    followeds << followed
    true
  end

  def unfollow(followed)
    !!(follower_relationships.find_by_followed_id(followed).try :destroy)
  end
end

authors_relationship.rb

class AuthorsRelationship < ActiveRecord::Base
  belongs_to :follower, foreign_key: :follower_id, class_name: Author
  belongs_to :followed, foreign_key: :followed_id, class_name: Author

  validate :ensure_different_targets
  validates_uniqueness_of :followed_id,
    scope: :follower_id,
    message: 'is already following the target'

  private

  def ensure_different_targets
    unless follower != followed
      errors.add(:follower_id, "can't be equal to followed_id")
    end
  end
end

author_spec.rb

RSpec.describe Author, type: :model do
  describe Author, '#follow' do

    before :all do
      DatabaseCleaner.start
      @bieber = Author.create!(name: 'Justin Bieber', screen_name: 'justinbieber')
      @teen = Author.create!(name: 'Aya No', screen_name: 'Ayano2327')
    end

    before :each do
      @bieber.reload
      @bieber.followers.reload
      @bieber.followeds.reload

      @teen.reload
      @teen.followers.reload
      @teen.followeds.reload
    end

    after :all do
      DatabaseCleaner.clean
    end

    context 'without followers yet' do
      it 'returns true' do
        result = @teen.follow @bieber
        ap AuthorsRelationship.all
        expect(result).to be true
      end

      it 'should be following after call' do
        ap AuthorsRelationship.all
        expect(@teen.followeds).to eq [@bieber]
      end
    end
  end
end

第二次测试失败。这是我从ap获得的输出:

[
    [0] #<AuthorsRelationship:0x00000002355f00> {
                 :id => 15,
        :follower_id => 22,
        :followed_id => 21,
         :created_at => Mon, 04 Apr 2016 21:25:04 UTC +00:00,
         :updated_at => Mon, 04 Apr 2016 21:25:04 UTC +00:00
    }
]
.[]

这篇文章rspec testing has_many :through and after_save没有解决问题。

2 个答案:

答案 0 :(得分:1)

使用DatabaseCleaner

before :all并不可靠,因此您的数据可能在每次测试之间实际被擦除。

此外,这是一个为什么保持测试完全独立的一个例子,这是一个很好的做法。给定的测试用例不应该依赖于套件中任何其他测试的状态或输出,这不是这里的情况。还要记住,RSpec测试可以按随机顺序运行。因此,即使您的代码按预期工作,也可能不会每次都通过。

我会将原before :all替换为before :each并将@teen.follow(@bieber)行提取到before :each,这应该可以解决问题。另请检查您的配置。你在使用交易设备吗?您的spec_helper文件中的数据库清理程序配置是什么样的?

修改

根据您的评论,我建议在使用数据库事务的测试之间清理数据库:

我发现以下配置对我有用:

config.before(:suite) do
  DatabaseCleaner.clean_with :truncation
end

config.after(:suite) do
  DatabaseCleaner.clean_with :truncation
end

config.after(:all, type: :feature) do |example|
  DatabaseCleaner.clean_with :truncation
end

config.before(:each) do |example|
  DatabaseCleaner.strategy = if example.metadata[:js]
                                 :truncation
                               else
                                 :transaction
                               end

  DatabaseCleaner.start
end

config.after(:each) do
  DatabaseCleaner.clean
end

答案 1 :(得分:1)

据安东尼说,我把测试分开了。我对这种行为感到失望。现在这是我的代码:

RSpec.describe Author, type: :model do
  describe Author, '#follow' do

    before :each do
      @bieber = Author.create!(name: 'Justin Bieber', screen_name: 'justinbieber')
      @teen = Author.create!(name: 'Aya No', screen_name: 'Ayano2327')
    end

    context 'when already following' do
      before :each do
        @teen.follow @bieber
      end

      it 'should raise when called' do
        expect { @teen.follow @bieber }.to raise_error(ActiveRecord::RecordInvalid)
      end

      it 'should have made the teen follow bieber' do
        expect(@teen.followeds).to eq [@bieber]
      end

      it 'should have made bieber followed by the teen' do
        expect(@bieber.followers).to eq [@teen]
      end
    end
  end
end