在不同类上模拟新方法的最佳实践是什么

时间:2014-11-18 18:03:03

标签: ruby unit-testing rspec

我有下一个场景:

module Module
  class CommandPattern
    def initialize(value)
      command = []
      @var = value['something']
      @abc = value['abc']
      @command << value
    end

    def add(value)
      @command << value
    end

    def get_command
      @command
    end
  end
end

module Module
  class Implementator
    def initialize(value)
      @value = value
    end

    def method_to_test(argument)

      var = "command1"
      cmd = CommandPattern.new(var)
      var2 = "command2"
      cmd.add(var2)
      var3 = argument
      cmd.add(var3)

      commands = var + var2 + var3
      commands
    end
  end
end

所以,当我测试Module :: B.method_I_want_to_test时,模拟“var = A.new(some_stuff)”的最佳做法是什么?除了重构并将这一行移到单独的方法之外,还有一些很好的方法吗?

关于这个问题的一点背景 - 这个样式(Module :: ClassA和Module :: ClassB) - 我正在使用http://naildrivin5.com/gli/,这个方法的原因是A类实际上是在实现Command Pattern。 / p>

所以问题显然是由于错误的方式尝试编写规范。

我之前做的是(在@spickermann建议的路上):

RSpec.describe Module::Implementator do

  describe "#method_to_test" do
    let(:command_argument)       { "command" }
    let(:cmnd)     { double(CommandPattern, :new => command_argument, :add => command_argument)}

    subject(:method_to_test) do
      Implementator.new("value").method_to_test("dejan")
    end

    before do
      allow(CommandPattern).to receive(:new).with(any_args).and_return(cmnd)
      allow(CommandPattern).to receive(:add).with(any_args).and_return(cmnd)
    end

    it 'does something' do
      expect{ method_to_test }.not_to raise_error
    end

    it 'does something else' do
      result = method_to_test
      expect(result).to eq("command1command2dejan")
    end
  end
end

问题显然是在测试Module :: Implementator,没有意识到我可以在我的RSpec.describe块周围放置模块并解决我的第一个问题:

module Module
  RSpec.describe Implementator do

  describe "#method_to_test" do
    let(:command_argument)       { "command" }
    let(:cmnd)     { double(CommandPattern, :new => command_argument, :add => command_argument)}

    subject(:method_to_test) do
      Implementator.new("value").method_to_test("dejan")
    end

    before do
      allow(CommandPattern).to receive(:new).with(any_args).and_return(cmnd)
      allow(CommandPattern).to receive(:add).with(any_args).and_return(cmnd)
    end

    it 'does something' do
      expect{ method_to_test }.not_to raise_error
    end

    it 'does something else' do
      result = method_to_test
      expect(result).to eq("command1command2dejan")
    end
  end

  end
end

我遇到的另一个问题是保持YAML结构的全局变量,我错过了在spec_helper.rb中看到并声明

但是,感谢@ spickermann的建议,问题解决了。

1 个答案:

答案 0 :(得分:1)

我会从这样的事情开始:

describe '#method_I_want_to_test' do
  let(:something) { # whatever something needs to be }
  let(:a)         { double(A, # methods you need from a) }

  subject(:method_I_want_to_test) do
    B.new(something).method_I_want_to_test
  end

  before do
    allow(A).to receive(:new).with(something).and_return(a)
  end

  it 'does what I expect' do
    expect(method_I_want_to_test).to eq(# what do_semething_else returns)
  end
end

有趣的部分是before块,它在new上存根A方法。它始终返回let(:a)行中定义的double,而不是A

的实例