编写RSpec以隔离和测试类

时间:2017-05-05 13:19:11

标签: ruby rspec rubygems

为使用类的gem编写单元测试的最佳方法是什么?我已经编写了一个宝石,可以在初始化过程中完成包含外部集成的工作。在编写测试时,我不想初始化所有内容,我只是想测试某些功能。

例如,我有lib/foomon.rb

class FooMon
  def initialize
    @log = Logger.new("app.log","daily")
    pollingClient = SomePoller.new(remote_service_url)
    pollingClient.poll do |response|
       processResponse(response)
    end
  end

  private 

  def processResponse(res)
    {:response => res.body}
  end
end

现在我想测试processResponse功能。我采取的方法是删除private修饰符,然后将代码从initialize移动到新的setup方法。应用程序的主要入口点然后调用设置,但我的测试不是(他们在@foomon = FooMon.new中只有before)。然后规范可以测试@foomon.processResponse

这是最好的方法吗?

2 个答案:

答案 0 :(得分:1)

过去我做过这个时,我倾向于使用RSpec Mocks (doubles)。在你的特定情况下,我认为,存在#body的双精度可以解决问题。

RSpec.describe FooMon do
  before do
    @foomon = FooMon.new
  end

  it "should process the response" do
    response = instance_double("Response")
    expect(response).to receive(:body).once.and_return("I ain't got no body ...")

    expect {response_body = @foomon.send(:process_response, response)}.not_to raise_error
    expect response_body == "I ain't got no body ..."
  end
end

免责声明:使用老化的内存在浏览器中编写。可能需要调试。

这是我能抓住的最好的real-life, working example。希望这有助于提供一些颜色。

答案 1 :(得分:1)

规范应该只测试公共方法,如果你实际删除了这个类的私有修饰符processResponce可以测试,否则没有什么可以在那里测试。

关于班级的外部整合,在单元测试方法中,你应该只测试班级本身的回报。

在这种情况下,您应该只测试从processResponce返回的内容,其他类(如LoggerSomePoller生成的所有逻辑都应该在其他规范上进行测试,而不是这个。

在这种情况下,我建议更改初始化程序中的逻辑并将其移动到单独的公共方法。因此,该方法可以负责将消息发送给客户端,并且更容易测试。

您可以使用double模拟这些类的行为。像这样:

let(:poller) { double(:poller)
let(:responce) { double(:responce, body: "message") }

before do
  allow(poller).to receive(:poll).and_yield(responce)
  allow(SomePoller).to response(:new) { poller }
end

it 'send a message to the client' do
  expect(response).to receive(:body).once
  subject.send_client
end

有了这个,你只能测试班级职责,并且说明你的考试太复杂了。

参考文献:

https://www.relishapp.com/rspec/rspec-mocks/v/3-6/docs/ https://relishapp.com/rspec/rspec-mocks/docs/configuring-responses/yielding#yield-an-argument