首先,我知道在单元测试中有多个断言是不好的做法。
但有时你需要测试一些原子事务。作为一个简化示例,让我们来看一些具有Account类的银行应用程序:
class Account
attr_accessor :balance
def transfer(to_account, amount)
self.balance -= amount
to_account.balance += amount
Audit.create(message: "Transferred #{amount} from #{self.number} to #{to_account.number}."
end
end
在这种情况下,我想一起检查3件事:
amount
amount
测试@account.transfer
方法的最佳方法是什么?
答案 0 :(得分:2)
在这种情况下,我想一起检查3件事:
我认为你真正想要的是在某些条件下描述这些事物的行为,从而确保行为符合你的规范。这可能意味着事情一起发生;或者它可能意味着某些事情只发生在一组条件而不是其他条件中,或者一个异常导致所有事情都回滚到其原始状态。
除了让事情变得更快之外,在一次测试中拥有所有断言是没有魔力的。 除非您面临严重的性能损失(通常在全栈测试中发生),否则每次测试使用一个断言要好得多。
RSpec可以直接提取测试设置阶段,以便为每个示例重复:
class Account
attr_accessor :balance
def transfer(to_account, amount)
self.debit!(amount)
to_account.credit!(amount)
Audit.create!(message: "Transferred #{amount} from #{self.number} to #{to_account.number}."
rescue SomethingBadError
# undo all of our hard work
end
end
describe Account do
context "when a transfer is made to another account" do
let(:other_account} { other_account }
context "and the subject account has sufficient funds" do
subject { account_with_beaucoup_bucks }
it "debits the subject account"
it "credits the other account"
it "creates an Audit entry"
end
context "and the subject account is overdrawn" do
subject { overdrawn_account }
it "does not debit the subject account"
it "does not credit the other account"
it "creates an Audit entry" # to show the attempted transfer failed
end
end
end
如果“快乐路径”中的所有三个测试都通过了,那么它们都“一起发生”,因为初始系统状态在每种情况下都是相同的。
但是,当出现问题时,您还需要确保不会发生的事情,并且系统会恢复到原始状态。有多个断言可以很容易地看到它按预期工作,当测试失败时,确切 它们失败了。
答案 1 :(得分:1)
每次测试多个断言并不总是坏习惯。如果多个断言验证了相同的行为,那么它就没有问题。尝试在同一测试中验证多个行为时存在问题。 当然,每次测试有多个断言存在一些风险。其中之一是您可能会意外地从以前的测试集中保留值,这会以奇怪的方式使先前的测试无效。 此外,当一个断言为假时,所有其他断言都不会被执行,这可能会导致难以理解发生了什么。但要合理,你可以有多个断言断言相同的行为,最好是短行为,没有额外的设置。
在你带来的简单案例中,我会使用多个断言,因为它非常简单。但当然它可以变得更复杂,比如负余额,不同类型的账户和东西。那么最好使用一个(最好的)断言进行不同的测试。我会像这样组织它:
1在所有这些可能性中测试审核;
1来测试当前to_account的行为(最简单的情况);
由于审核测试非常简单,无需额外设置,因此您还可以将其与Account和to_account一起进行测试。