Ruby on rails 4 使用rspec测试调用作用域的类方法

Ruby on rails 4 使用rspec测试调用作用域的类方法,ruby-on-rails-4,rspec3,Ruby On Rails 4,Rspec3,我已经为我的作用域创建了rspec测试(scope1、scope2和scope3),它们按预期通过,但我还想为我拥有的类方法添加一些测试,该类方法实际上是从我的控制器调用的(控制器通过该类方法间接调用作用域): 当我知道它们已经通过时,在这个类方法中为每个场景运行相同的作用域测试似乎有点多余,所以我假设我真的只需要确保根据传递到这个类方法中的参数调用/应用不同的作用域 有人能告诉我这个rspec测试是什么样的吗 我想这可能是类似于 expect_any_instance_of(MyModel.m

我已经为我的作用域创建了rspec测试(
scope1
scope2
scope3
),它们按预期通过,但我还想为我拥有的类方法添加一些测试,该类方法实际上是从我的控制器调用的(控制器通过该类方法间接调用作用域):

当我知道它们已经通过时,在这个类方法中为每个场景运行相同的作用域测试似乎有点多余,所以我假设我真的只需要确保根据传递到这个类方法中的参数调用/应用不同的作用域

有人能告诉我这个rspec测试是什么样的吗

我想这可能是类似于

expect_any_instance_of(MyModel.my_class_method(arg1, nil)).to receive(:scope1).with(arg1, nil)
但这不起作用


我还希望确认这是在这种情况下测试所需的全部内容,因为我已经测试了作用域,无论如何都会重新出现。

您编写的Rspec代码实际上是在测试您的方法的内部实现。在给定参数的情况下,您应该测试该方法是否返回您希望它返回的内容,而不是它是否以某种方式返回。这样,你的测试就不会那么脆弱了。例如,如果更改调用的
scope1
,则不必重写
my\u class\u方法
测试

为此,我将创建大量的类实例,然后使用各种参数调用该方法,并检查结果是否符合预期

我不知道
scope1
scope2
做什么,所以我举了一个例子,其中参数是模型的
name
属性,scope方法只检索除具有该名称的模型之外的所有模型。显然,无论您的实际参数和
scope
方法是什么,您都应该将其放在测试中,并相应地修改预期结果

由于
self.all
调用实际上返回一个ActiveRecord关联,因此不会与预期的数组匹配,因此我使用了
to__ary
方法来获得预期的结果。您可能会使用
includes
does\u includes
而不是
eq
,但您可能关心顺序或其他事项

describe MyModel do
  describe ".my_class_method" do
    # Could be helpful to use FactoryGirl here
    # Also note the bang (!) version of let
    let!(:my_model_1) { MyModel.create(name: "alex") }
    let!(:my_model_2) { MyModel.create(name: "bob") }
    let!(:my_model_3) { MyModel.create(name: "chris") }

    context "with nil arguments" do
      let(:arg1) { nil }
      let(:arg2) { nil }
      it "returns all" do
        expected = [my_model_1, my_model_2, my_model_3]
        expect_my_class_method_to_return expected
      end
    end

    context "with a first argument equal to a model's name" do
      let(:arg1) { my_model_1.name }
      let(:arg2) { nil }
      it "returns all except models with name matching the argument" do
        expected = [my_model_2, my_model_3]
        expect_my_class_method_to_return expected
      end

      context "with a second argument equal to another model's name" do
        let(:arg1) { my_model_1.name }
        let(:arg2) { my_model_2.name }
        it "returns all except models with name matching either argument" do
          expected = [my_model_3]
          expect_my_class_method_to_return expected
        end
      end
    end
  end

  private 

  def expect_my_class_method_to_return(expected)
    actual = described_class.my_class_method(arg1, arg2).to_ary
    expect(actual).to eq expected
  end
end

您编写的Rspec代码实际上是在测试您的方法的内部实现。在给定参数的情况下,您应该测试该方法是否返回您希望它返回的内容,而不是它是否以某种方式返回。这样,你的测试就不会那么脆弱了。例如,如果更改调用的
scope1
,则不必重写
my\u class\u方法
测试

为此,我将创建大量的类实例,然后使用各种参数调用该方法,并检查结果是否符合预期

我不知道
scope1
scope2
做什么,所以我举了一个例子,其中参数是模型的
name
属性,scope方法只检索除具有该名称的模型之外的所有模型。显然,无论您的实际参数和
scope
方法是什么,您都应该将其放在测试中,并相应地修改预期结果

由于
self.all
调用实际上返回一个ActiveRecord关联,因此不会与预期的数组匹配,因此我使用了
to__ary
方法来获得预期的结果。您可能会使用
includes
does\u includes
而不是
eq
,但您可能关心顺序或其他事项

describe MyModel do
  describe ".my_class_method" do
    # Could be helpful to use FactoryGirl here
    # Also note the bang (!) version of let
    let!(:my_model_1) { MyModel.create(name: "alex") }
    let!(:my_model_2) { MyModel.create(name: "bob") }
    let!(:my_model_3) { MyModel.create(name: "chris") }

    context "with nil arguments" do
      let(:arg1) { nil }
      let(:arg2) { nil }
      it "returns all" do
        expected = [my_model_1, my_model_2, my_model_3]
        expect_my_class_method_to_return expected
      end
    end

    context "with a first argument equal to a model's name" do
      let(:arg1) { my_model_1.name }
      let(:arg2) { nil }
      it "returns all except models with name matching the argument" do
        expected = [my_model_2, my_model_3]
        expect_my_class_method_to_return expected
      end

      context "with a second argument equal to another model's name" do
        let(:arg1) { my_model_1.name }
        let(:arg2) { my_model_2.name }
        it "returns all except models with name matching either argument" do
          expected = [my_model_3]
          expect_my_class_method_to_return expected
        end
      end
    end
  end

  private 

  def expect_my_class_method_to_return(expected)
    actual = described_class.my_class_method(arg1, arg2).to_ary
    expect(actual).to eq expected
  end
end

谢谢@rob-w。我只是想知道,如果我已经对每个作用域进行了测试,我是否应该将这些更改为使用共享的示例/
的行为类似于
,以便根据应用于类方法的参数相应地将它们重新用于该类方法…如果这样做有意义的话?@user1116573是的,这样做有意义,并且会产生非常干净的测试代码。请注意,您将把测试绑定到
scope1
scope2
方法。6个月后,如果您或其他人更改
scope1
的操作并相应地修复
scope1
测试,则
my_class_方法的测试也将通过。根据业务逻辑,这可能是好事,也可能是坏事。谢谢@rob-w。我只是想知道,如果我已经对每个作用域进行了测试,我是否应该将这些更改为使用共享的示例
/
的行为类似于
,以便根据应用于类方法的参数相应地将它们重新用于该类方法…如果这样做有意义的话?@user1116573是的,这样做有意义,并且会产生非常干净的测试代码。请注意,您将把测试绑定到
scope1
scope2
方法。6个月后,如果您或其他人更改
scope1
的操作并相应地修复
scope1
测试,则
my_class_方法的测试也将通过。根据业务逻辑,这可能是好事,也可能是坏事。谢谢@rob-w。我只是想知道,如果我已经对每个作用域进行了测试,我是否应该将这些更改为使用共享的示例
/
的行为类似于
,以便根据应用于类方法的参数相应地将它们重新用于该类方法…如果这样做有意义的话?@user1116573是的,这样做有意义,并且会产生非常干净的测试代码。请注意,您将把测试绑定到
scope1
scope2
方法。6个月后,如果您或其他人更改
scope1
的操作并相应地修复
scope1
测试,则
my_class_方法的测试也将通过。根据业务逻辑,这可能是好事,也可能是坏事。