我正在制定一个包含几个步骤的销售流程 - 折扣,验证,付款,交付等。如果在此过程中出现问题,我希望能够中止操作,但仍然可以退回销售反对来电者。
代码工作得很好,使用throw / catch(但是我很乐意使用raise / rescue,如果有帮助的话。)问题是测试它 - 理想情况下,我能编写一个测试说“如果出现问题,销售将停止处理”。相反,似乎我能做的最好就是说“如果出现问题,它会抛出:完成”或“如果在第2步出现问题,则不会完成步骤3和4”。有没有其他方法可以实际测试更一般的情况?
这是我正在使用的代码的略微修改版本。使用刘海的方法是可以调用fail!
或succeed!
# sale.rb
class Sale
attr_accessor :status, :amount, :discount_code
def process!
catch :done do
validate_params!
amount = apply_code!(discount_code)
verify_delivery!
charge_money!
deliver_goods!
end
self
end
def fail!(error_message: nil)
Rails.logger.error(error_message)
status = :failure
throw :done
end
def succeed!
status = :success
throw :done
end
end
我想避免的那种RSpec测试的例子:
# sale_spec.rb
describe 'Sale' do
describe '#process!' do
context 'when verification fails' do
before do
allow(sale).to receive(:verify_delivery) { sale.fail! }
end
it "doesn't charge money" do
expect(sale).to_not receive(:charge_money!)
sale.process!
end
it "doesn't deliver the goods" do
expect(sale).to_not receive(:deliver_goods!)
sale.process!
end
end
end
end
有没有办法可以更普遍地测试这个?
答案 0 :(得分:1)
我不会为您的特定代码提供解决方案,但我们可以考虑一般的问题。您有一组行动,共同负责销售过程。解决方案是使每个动作独立并使它们流入。如果出错,每个动作都可以恢复。在这种情况下,整个过程将停止在错误指令上,并通过调用其恢复方法来逐步退回操作。所有操作都应该共享一些可以存储对象的上下文(并将它们传递给下一个交互器)。这样,您就可以将流程标记为失败,并稍后可以访问其上下文。
你应该检查一个很棒的lib - https://github.com/collectiveidea/interactor。基本上,您可以将交互器定义为块,然后从这些类中组成一个组织器。它允许您使用FooOrganizer.call(some_context)
来返回上下文对象。此上下文对象响应success?
和failure?
方法(您可以在任何交互器内调用context.fail!
以停止继续,将进程标记为失败并执行回滚)。您还可以从外部访问上下文中的对象(以便稍后可以访问销售对象)。
在这种方法中,你可以只测试组织者是否成功和/或在其上下文中检查进行中的对象。