Ruby中的RSpec和对象初始化

时间:2016-10-06 19:11:08

标签: ruby-on-rails ruby rspec

有些东西告诉我,我在测试中缺少一个关键概念/想法,或者(天堂禁止)ruby如何初始化对象。

我有一个接受两个参数的类方法,并返回所述类的实例。所以它看起来像这样:

    class Manager

      def self.run(first_arg, second_arg)
        new(first_arg, second_arg)
      end
    end

这是我的RSpec测试:

    RSpec.describe Manager, type: :api do

      let(:first_arg) { FactoryGirl.build_stubbed(:first_arg) }
      let(:second_arg) { AccountMailer.new }

      describe '::run' do 
        it "accepts two arguments" do 
          expect(Manager).to receive(:run).with(first_arg, second_arg)
          Manager.run(first_arg, second_arg)
        end

        it "instantiates the class with 2 arguments" do 
          expect(Manager).to receive(:new).with(first_arg, second_arg)
          Manager.run(first_arg, second_arg)
        end
      end
    end

由于(我相信)方法:initialize被new调用,我将代码更新为:

   class Manager 
     # add attr_reader for read access 
     attr_reader :first_arg, :second_arg 

     def initialize(first_arg, second_arg)
       @first_arg = first_arg
       @second_arg = second_arg
     end

     def self.run(first_arg, second_arg)
       new(first_arg, second_arg)
     end
   end

我的测试失败并返回此错误:

    1) Manager#run instantiates the class
       Failure/Error: expect(Manager).to receive(:new).with(first_arg, second_arg)
       Wrong number of arguments. Expected 0, got 2.

我的主要问题是:

为什么看起来我传递给初始化的方法没有在rspec中被选中?我希望测试能够通过,因为Manager.new,如果在类中定义了initialize,如果没有传递2个参数,则会失败。

任何人都可以指出我在这里缺少的东西吗?感谢您的反馈。谢谢。

1 个答案:

答案 0 :(得分:6)

予。我能用这样的代码重新创建你的问题

class Manager
  def self.run(a, b)
    new(a, b)
  end
end

RSpec.configure do |config|
  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end
end

RSpec.describe Manager, type: :api do
  let(:a) { 1 }
  let(:b) { 2 }

  describe '::run' do
    it 'instantiates the class with 2 arguments' do
      expect(Manager).to receive(:new).with(a, b)
      Manager.run(a, b)
    end
  end
end

结果是:

1) Manager#run instantiates the class with 2 arguments
   Failure/Error: expect(Manager).to receive(:new).with(a, b)
     Wrong number of arguments. Expected 0, got 2.

这是因为verifying functionality。启用此设置(应该是)时,RSpec将确保该对象实现了被存根/模拟的接口。在这种情况下,RSpec会在行expect(Manager).to receive(:new).with(a, b)上抛出一个错误,因为它实际上会查看Manager类并检查初始化是否可以使用2个参数。

如果您将经理更改为如下所示,则示例将通过:

class Manager
  attr_reader :a, :b

  def initialize(a, b)
    @a = a
    @b = b
  end

  def self.run(a, b)
    new(a, b)
  end
end

II。但是你真的不需要使用模拟这样的功能。如果您只是检查是否返回了正确类型的实例,那么最好只查看真实的实例。

RSpec.describe Manager, type: :api do
  let(:a) { 1 }
  let(:b) { 2 }

  describe '::run' do
    subject { described_class.run(a, b) }

    it 'instantiates the class with 2 arguments' do
      expect(subject).to be_an_instance_of(Manager)
    end

    it 'sets a to the first argument' do
      expect(subject.a).to eq(a)
    end

    it 'sets b to the second argument' do
      expect(subject.b).to eq(b)
    end
  end
end

III。在这个例子中:

expect(Manager).to receive(:run).with(first_arg, second_arg)
Manager.run(first_arg, second_arg)

您设置了一个断言,然后立即调用代码来传递该断言。所以没有真正测试任何东西。

正确模拟/存根是相当先进的测试概念,很容易弄错,所以如果你可以不用它,只是不用它,它会让事情变得更容易。

如果您想了解更多有关测试/何时模拟的信息。我推荐Sandi Metz的演讲。 https://www.youtube.com/watch?v=URSWYvyc42M