【问题标题】:RSpec + DatabaseCleaner help -- teardown happening prematurelyRSpec + DatabaseCleaner 帮助——过早地进行拆解
【发布时间】:2011-05-24 14:21:38
【问题描述】:

我对 RSpec 一直坚持基于 xUnit 的测试框架有点迷茫,但我正在尝试一下。

规范编写方式的嵌套性质让我对我应该在哪里进行数据库设置/拆卸感到有些头疼。

根据DatabaseCleaner自述文件:

Spec::Runner.configure do |config|

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

  config.before(:each) do
    DatabaseCleaner.start
  end

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

end

现在我不能使用事务,因为我在我的代码中使用它们,所以我只是坚持截断,但这不应该在这里也不应该在那里。

我有这个:

RSpec.configure do |config|
  config.mock_with :rspec

  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

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

这里的问题是,当我尝试在下面的 describeit 块中使用它们时,我在 subjectlet 块中创建的任何固定装置都已经消失(从数据库中)。

例如(使用 Machinist 创建夹具......但这不应该是相关的):

describe User do
  describe "finding with login credentials" do
    let(:user) { User.make(:username => "test", :password => "password") }
    subject { User.get_by_login_credentials!("test", "password") }
    it { should == user }
  end
end

我正在为如何嵌套这些 describesubject 和其他块而苦苦挣扎,所以也许这是我的问题,但基本上这失败了,因为当它试图从数据库中获取用户时,由于调用了after(:each) 钩子,它已经被删除了,大概是在let 之后?

【问题讨论】:

  • 顺便说一句,您应该仍然可以使用事务来清理数据库。在大多数数据库中,事务可以嵌套(MySQL 是这里值得注意的例外——这只是另一种糟糕的方式)。

标签: ruby-on-rails ruby rspec datamapper


【解决方案1】:

如果您要同时使用subjectlet,您需要了解如何/何时调用它们。在这种情况下,subjectlet 生成的user 方法之前被调用。问题不在于在调用subject 之前从数据库中删除了该对象,而是在那时甚至没有创建它。

如果您使用 let! 方法,您的示例将起作用,该方法添加了一个 before 挂钩,该挂钩在示例之前隐式调用 user 方法(因此在调用 subject 之前)。

也就是说,我建议您停止挣扎并使用 RSpec 已经公开的更简单的 API:

describe User do
  it "finds a user with login credentials" do
    user = User.make(:username => "test", :password => "password")
    User.get_by_login_credentials!("test", "password").should eq(user)
  end
end

这对我来说似乎要简单得多。

【讨论】:

  • 谢谢,我不理解这些块是如何/何时被调用的,这绝对是正确的。我最初只是将上面代码中的外部describe 视为Test::Unit::TestCase 子类,每个it 就像它是xUnit 框架中的测试方法一样。然后我开始阅读其他人的规格,他们使用subject ... 跟随一系列it ... should 块,这让我确信我做错了。我想我需要把头撞在墙上一段时间才能有意义;)
  • 很快,如果我需要一个由describe 块内的所有测试共享的夹具,那么最好的地方是哪里?只需使用before(:each)?所有这些其他方法都让我偏离了轨道!
  • 很高兴它开始为您清理。 subject 的初衷是在一个像 it { should validate_presence_of(:some_attr) } 这样的内衬中支持 shoulda 匹配器。它并不是真的打算出现在规格中。它记录在案、公开且可用,但我在人们的规范中看到的越多,我就越认为它不应该存在,因为美学原因和随之而来的明显混淆尝试将它与 letbefore 挂钩一起使用。
  • 普通夹具的最佳位置是在before(:each) 挂钩中。这很简单,意图揭示,而且很有效:)
  • 你应该看看让!它会自动将给定的块作为每个示例的前钩子执行。
【解决方案2】:

你写道:

这里的问题是,当我尝试在下面的描述或它块中使用它们时,我在主题中创建的任何固定装置或让块已经消失(从数据库中)。

没错,就是这样。 (而且您使用的不是通常的 Rails 意义上的固定装置,而是工厂——也一样,因为 Rails 固定装置很烂。)

每个单独的规范(即每个it 块)都从原始数据库开始(或应该开始)。否则你的测试会泄漏状态并失去原子性。所以你应该在你需要的规范中创建你需要的每条记录(或者,正如大卫所说,在before 块中以减少重复)。

至于组织你的规范......以任何有意义的方式去做。通常整个班级会有一个外部describe 块,内部describe 块用于需要通用设置的相关行为或规范组。每个describe 上下文都可以有自己的beforeafter 块。这些会像您期望的那样嵌套,因此执行顺序类似于
before
内部before
规格
内部after
after

如果您想查看包含大量 RSpec 规范和 Cucumber 故事的项目(尽管每个版本都稍旧),请查看 http://github.com/marnen/quorum2

【讨论】:

  • 感谢您的解释,非常感谢。一旦我开始,一切都开始变得有意义。只是一些初步的误解。使用 RSpec 是一种乐趣! (我实际上是在大约 14 小时前开始回复这个问题的,我不知道那段时间发生了什么;)......我想我在打字的过程中切换了标签)。
  • 是的,我非常喜欢 RSpec。我发现它的语法鼓励你考虑行为,而不是实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
相关资源
最近更新 更多