如何使用RSpec测试在控制器方法中调用的类

时间:2019-01-20 00:39:52

标签: ruby-on-rails ruby-on-rails-4 rspec-rails rspec3

我正在测试我的控制器,以确保调用了一个库类,并且该功能按预期运行。 注意:这可能是在其他地方提出的,但我需要针对我的具体问题寻求帮助。我也很乐意指出如何最好地对此进行测试。

为了更好地解释我的问题,我将通过代码提供上下文。 我的/Lib文件夹中有一个类,它发出事件(如果您不明白那是什么意思,请不要介意)。该类看起来像这样:

class ChangeEmitter < Emitter
  def initialize(user, role, ...)
    @role = role
    @user = user
    ...
  end

  def emit(type)
    case type
    when CREATE
      payload = "some payload"
    when UPDATE
      payload = "some payload"
    ...
    end

    send_event(payload, current_user, ...)
  end
end

这是我在控制器中使用它的方式:

class UsersController < ApplicationController

  def create
    @user = User.new(user_params[:user])
    if @user.save
      render :json => {:success => true, ...}
    else
      render :json => {:success => false, ...}
    end
    ChangeEmitter.new(@user, @user.role, ...).emit(ENUMS::CREATE)
  end
end

很抱歉,如果某些代码没有意义,我试图在不公开太多代码的情况下解释问题。

这是我为测试所做的尝试:

describe UsersController do
  before { set_up_authentication }

  describe 'POST #create' do
    it "calls the emitter" do
      user_params = FactoryGirl.attributes_for(:user)
      post :create, user: user_params

      expect(response.status).to eq(200)
      // Here is the test for the emitter
      expect(ChangeEmitter).to receive(:new)
    end
  end
end

我希望ChangeEmitter类会收到new,因为它会在创建操作执行后立即调用。

相反,这是我得到的错误:

(ChangeEmitter (class)).new(*(any args))
       expected: 1 time with any arguments
       received: 0 times with any arguments

以上代码中我缺少什么,为什么该类没有收到新的。有没有更好的方法来测试上述功能?请注意,这是Rspec。您的帮助将不胜感激。谢谢。

2 个答案:

答案 0 :(得分:2)

您需要将expect(ChangeEmitter).to receive(:new)代码放在发布请求上方。当您期望类接收方法时,您的“期望”语句将在调用控制器之前。期望将来会发生一些事情。因此您的测试应类似于:

it "calls the emitter" do    
  user_params = FactoryGirl.attributes_for(:user)

  expect(ChangeEmitter).to receive(:new)

  post :create, user: user_params

  expect(response.status).to eq(200)
end

编辑

在注意到您对“ new”的调用之后,您将“ emit”操作链接起来后,我意识到我需要针对您的特定用例更新答案。您需要返回一个可以被调用的对象(我通常返回一个间谍或一个双精度对象)。有关间谍和双打之间区别的更多信息,请查看: https://www.ombulabs.com/blog/rspec/ruby/spy-vs-double-vs-instance-double.html

基本上,间谍将接受在其上调用的任何方法并返回自身,而对于double,则必须指定它可以接受的方法以及返回的方法。对于您的情况,我认为间谍起作用。

所以您想要这样做:

it "calls the emitter" do    

  user_params = FactoryGirl.attributes_for(:user)

  emitter = spy(ChangeEmitter)
  expect(ChangeEmitter).to receive(:new).and_return(emitter)

  post :create, user: user_params

  expect(response.status).to eq(200)
end

答案 1 :(得分:0)

我认为这样更有意义

describe 'POST #create' do
    it "calls the emitter" do
      user_params = FactoryGirl.attributes_for(:user)
      emitter_spy = spy(:emitter) #this doesn't need to be a specific name - only if you were using instance_double
      ChangeEmitter.stub(:new).and_return(emitter_spy)

      post :create, user: user_params

      expect(response.status).to eq(200)
      expect(emitter_spy).to have_received(:emit)
    end
  end