【问题标题】:testing a multi-step workflow in rspec在 rspec 中测试多步骤工作流程
【发布时间】:2010-12-13 16:10:19
【问题描述】:

我想了解使用 rspec 测试多步骤工作流的习惯用法或最佳实践。

让我们以“购物车”系统为例,购买过程可能在其中

  1. 当用户提交到购物篮并且我们没有使用 https 时,重定向到 https
  2. 当用户提交到购物篮并且我们使用 https 并且没有 cookie 时,创建并显示一个新购物篮并发回一个 cookie
  3. 当用户提交到购物篮并且我们使用 https 并且有一个有效的 cookie 并且新商品与第一个商品的产品不同时,在购物篮中添加一行并显示这两行
  4. 当用户提交到购物篮并且我们使用 https 并且有一个有效的 cookie 并且新商品与前一个产品是相同的产品时,增加该购物篮行的数量并显示这两行
  5. 当用户点击购物篮页面上的“结帐”并使用 https 并且有一个 cookie 并且购物篮非空且...
  6. ...

我读过http://eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/,它建议 i.a 每个“it 块”应该只包含一个断言:不要进行计算然后在同一个块中测试多个属性,而是在上下文中使用“之前”来创建(或检索)被测对象并将其分配给@some_instance_variable,然后将每个属性测试写为一个单独的块。这有点帮助,但在上面概述的情况下,测试步骤 n 需要为步骤 [1..n-1] 进行所有设置,我发现自己要么复制设置代码(显然不好),要么创建大量辅助函数具有越来越笨拙的名称(def create_basket_with_three_lines_and_two_products)并在每个步骤的 before 块中连续调用它们。

关于如何不那么冗长/乏味地执行此操作的任何提示?我很欣赏这个想法背后的一般原则,即每个示例不应依赖于先前示例留下的状态,但是当您测试多步骤过程并且任何步骤都可能出错时,为每个步骤设置上下文是不可避免地需要重新运行前 n 步的所有设置,所以...

【问题讨论】:

    标签: ruby rspec bdd


    【解决方案1】:

    这是一种可能的方法——定义一个对象,为每个步骤创建必要的状态,并为每个后续步骤传递它。基本上你需要为所有设置条件模拟/存根方法调用:

    class MultiStep
      def initialize(context)
        @context = context
      end
    
      def init_vars
        @cut = @context.instance_variable_get(:@cut)
      end
    
      def setup(step)
        init_vars
        method(step).call
      end
    
      def step1
        @cut.stub(:foo).and_return("bar")
      end
    
      def step2
        step1
        @cut.stub(:foo_bar).and_return("baz_baz")
      end
    end
    
    class Cut  # Class Under Test
      def foo
        "foo"
      end
      def foo_bar
        "foo_bar"
      end
    end
    
    describe "multiple steps" do
      before(:each) do
        @multi_stepper = MultiStep.new(self)
        @cut = Cut.new
      end
    
      it "should setup step1" do
        @multi_stepper.setup(:step1)
        @cut.foo.should == "bar"
        @cut.foo_bar.should == "foo_bar"
      end
    
      it "should setup step2" do
        @multi_stepper.setup(:step2)
        @cut.foo.should == "bar"
        @cut.foo_bar.should == "baz_baz"
      end
    
    end
    

    【讨论】:

      【解决方案2】:

      对于 OP 来说肯定为时已晚,但这对其他人来说可能很方便 - rspec-steps gem 似乎是为这种确切情况而构建的:https://github.com/LRDesign/rspec-steps

      看看https://github.com/railsware/rspec-example_stepshttps://github.com/jimweirich/rspec-given 可能也是值得的。我选择了 rspec 步骤,但我很着急,据我所知,这些其他选项实际上可能更好。

      【讨论】:

      • 既然您已经使用了 rspec-steps gem,您对解决多步尊重问题的结果满意吗?
      • @Angela:嗯,好问题。我简单地使用了它,但小组的其他人并不热衷于它,所以我们最终放弃了它并有更多的重复。我认为的基本原理是我们需要更多的可读性,但已经很久了......在这一点上,我发现真正的集成测试最容易编写为只执行单个“工作流”每个测试。不是最惯用或最流行的方法,但我喜欢可读性。
      • 我在 rspec 中看到只有一个 describe
      • 这取决于,我猜。但在只是一个顺序集成规范的情况下,可能是一个描述,然后是几个相对较大的测试块,有多个“预期”。不过,这是一个仍在进化的过程。我们只是还没有找到一种让集成规范按照我们想要的方式做事的好方法。我们的目标是制作非常强大的单元测试,并用一些稀疏/简单的集成规范来补充它们。一年后再问我,它可能会改变:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多