Rspec:在控制器中存根方法

18

我该如何为控制器中的create方法桩定方法?我需要为此编写规范,但我得到了这些错误。我需要检查控制器中的create方法是否在创建模型中的新公司记录之前执行validate_fbid方法。

错误:

1) Companies new company create with valid information#validate_fbid should have correct parameters and return value
 Failure/Error: CompaniesController.create.should_receive(:validates_fbid).with(company)
 NoMethodError:
   undefined method `create' for CompaniesController:Class
 # ./spec/requests/companies_spec.rb:38:in `block (5 levels) in <top (required)>'

  2) Companies new company create with valid information#validate_fbid should fbid validation passed
 Failure/Error: CompaniesController.create.stub(:validates_fbid).and_return('companyid')
 NoMethodError:
   undefined method `create' for CompaniesController:Class
 # ./spec/requests/companies_spec.rb:43:in `block (5 levels) in <top (required)>'

公司控制器

def create 
company = Company.new(params[:company])
verifyfbid = validate_fbid(company)

if verifyfbid != false
    if company.fbid.downcase == verifyfbid.downcase
        if company.save 
            @message = "New company created."
            redirect_to root_path
        else 
            @message = "Company create attempt failed. Please try again."
            render 'new' 
        end 
    else 
        @message = "Company create attempt failed. Invalid facebook id."
        render 'new' 
    end
else  
    @message = "Company create attempt failed. No such facebook id."
    render 'new'            
    end             
 end 

  private  
  def validate_fbid(company)
   uri = URI("http://graph.facebook.com/" + company.fbid)
   data = Net::HTTP.get(uri)
   username = JSON.parse(data)['username']      
   if username.nil?
    return false 
   else
    "#{username}"
   end
 end

请求/公司_spec.rb

context "#validate_fbid" do               
        #validate fbid
        let(:company){ Company.new(name:'Example Company', url: 'www.company.com', fbid: 'companyid', desc: 'Company desc' )}

        it "should have correct parameters and return value" do
            CompaniesController.create.should_receive(:validates_fbid).with(company)
                                .and_return('companyid')
        end

        it "should fbid validation passed" do               
            CompaniesController.create.stub(:validates_fbid).and_return('companyid')
            company.fbid.should_not be_nil
            company.fbid.should == 'companyid'
            company.save
            expect { click_button submit }.to change(Company, :count).by(1)
        end                                             
    end    
5个回答

25

当被测试用例所涉及时,您不希望对该方法进行存根

context "#validate_fbid" do
  #test the function here
  #don't stub
end

当你在控制器中测试"create"操作时,你可以对"validate_fbid"进行存根处理。
describe "post create" do
   ...
   CompaniesController.any_instance.stub(:validates_fbid).and_return('companyid')
   ...
end

希望对您有所帮助。

20
allow_any_instance_of(CompaniesController).to receive(:validates_fbid).and_return('companyid') 这段代码是针对 Rspec3 的。它的作用是允许 CompaniesController 的任何实例调用 validates_fbid 方法时返回字符串 'companyid'。 - ryan2johnson9

6

如果你正在测试控制器,你可以直接访问控制器:

controller.stub(:message) { 'this is the value to return' }

这个答案对我有用,因为当我桩方法时,我知道它需要一个参数,但我不知道参数是什么。我只想传递参数,所以我使用带有参数的块来覆盖(桩)该方法。 - Volte

6
当代码难以测试时,通常是因为它过于复杂。
您应该通过以下方式重构此代码:
- 将验证逻辑移动到新的“服务类”中,该类的单一职责是在 Facebook 上验证公司。 - 这将使验证功能独立于 Web 层,并且易于测试。 - 为服务类编写规范,以在隔离状态下测试此代码(无控制器)。 - 清理控制器逻辑 - 您不希望在控制器内部具有逻辑(经验法则:最多一个嵌套级别)。 - 控制器的规范也将更容易。
控制器代码可能如下所示:
def create
  company = Company.new(params[:company])
  verified = FbCompanyVerifier.new.verify(company)

  if verified and company.save
    # success logic
  else
    # fail logic
  end
end

嗨,感谢回复。这是否意味着我需要创建一个名为FbCompanyVerifier的类,该类具有“new”方法和“verify”方法?我想我不允许在控制器页面中创建类,那么我应该在哪个文件夹中创建“FbCompanyVerifier”?感谢澄清。我对此非常陌生。 - shoujo_sm
正确 - 一个带有实例方法“verify”的完整类,如果它不执行任何操作,则可以跳过“new”(初始化)方法。通常这是最佳实践,因此您可以使用一流对象/纯Ruby对象进行操作。 (有些人可能会使用具有类方法的类,这有其自身的优缺点)。 由于此类依赖于您的模型公司,因此您应该将其放在应用程序文件夹“app / services”中的某个位置。您可以在那里放置小的单一责任类,其中包含一两个方法,并且独立于Web层。 - jurglic
你也可以查看code_climate的博客文章,其中描述了类似的重构 - 将逻辑代码提取到新的类中,并编写简单的规范,然后在代码中将其用作PORO(普通旧Ruby对象)...: http://blog.codeclimate.com/blog/2013/07/23/testing-code-in-a-rails-initializer/ - jurglic
如果FbCompanyVerifier#verify执行HTTP请求(或任何其他你想在测试期间避免的副作用),那么如何进行测试以防止它发生?使用allow_any_instance_ofFbCompanyVerifier上吗? - wacha

4

1
您可以对一个controller方法进行存根:
allow(controller).to receive(...).with(...).and_return(...)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接