【问题标题】:Use of Spies for detecting method calls使用间谍检测方法调用
【发布时间】:2015-12-02 08:55:05
【问题描述】:

我是 RSpec 的新手。我正在使用以下(正确的)规范代码来确保在测试控制器中调用方法 findsame_director

require 'rails_helper'

describe MoviesController do
    describe 'Searching movies for same director: ' do
        before :each do
            @movie1 = instance_double('Movie', id:1, title:'movie1')
            @movie2 = instance_double('Movie', id:2, title:'movie2')
            @any_possible_id = 1
        end
        it 'should call the model method that finds the movie corresponding to the passed id.' do
            allow(@movie1).to receive(:same_directors).and_return [@movie1,@movie2]
            expect(Movie).to receive(:find).with(@any_possible_id.to_s).and_return @movie1
            get :directors, :id=>@any_possible_id
        end
        it 'should call the model method that finds the movies corresponding to the same director.' do
            expect(@movie1).to receive(:same_directors).and_return [@movie1,@movie2]
            allow(Movie).to receive(:find).with(@any_possible_id.to_s).and_return @movie1
            get :directors, :id=>@any_possible_id
        end

这工作正常,但如您所见,两个it-子句包含重复的get :directors, :id=>@any_possible_id 行。鉴于控制器中语句的顺序(请参阅问题末尾的控制器代码),get 必须出现在 it 子句的末尾。挑战在于通过将其移至before-clause 来将其干燥。 当然,最好的解决方案是将其移至 after-clause (我意识到,当我写这个问题时,我做到了并且它有效。)但我现在想要了解为什么我使用 Spies这里不工作。

我对间谍的(失败)尝试是:

describe MoviesController do
    describe 'Searching movies for same director: ' do
        before :each do
            @movie1 = instance_double('Movie', id:1, title:'movie1')
            @movie2 = instance_double('Movie', id:2, title:'movie2')
            @any_possible_id = 1
            @spyMovie = class_spy(Movie)
            @spymovie1 = instance_spy(Movie)
            get :directors, :id=>@any_possible_id
        end
        it 'should call the model method that finds the movie corresponding to the passed id.' do
            allow(@spymovie1).to receive(:same_directors)#.and_return [@movie1,@movie2]
            expect(@spyMovie).to have_received(:find).with(@any_possible_id.to_s)#.and_return @movie1
        end
        it 'should call the model method that finds the movies corresponding to the same director.' do
            expect(@spymovie1).to have_received(:same_directors)#.and_return [@movie1,@movie2]
            allow(@spyMovie).to receive(:find).with(@any_possible_id.to_s)#.and_return @movie1
        end

现在,第二段代码可能有几个问题,但是我在运行rspec 时遇到的错误是:

  1) MoviesController Searching movies for same director:  should call the model method that finds the movie corresponding to the passed id.
     Failure/Error: expect(@spyMovie).to have_received(:find).with(@any_possible_id.to_s)#.and_return @movie1
       (ClassDouble(Movie) (anonymous)).find("1")
           expected: 1 time with arguments: ("1")
           received: 0 times
     # ./spec/controllers/movies_controller_spec.rb:32:in `block (3 levels) in <top (required)>'

  2) MoviesController Searching movies for same director:  should call the model method that finds the movies corresponding to the same director.
     Failure/Error: expect(@spymovie1).to have_received(:same_directors)#.and_return [@movie1,@movie2]
       (InstanceDouble(Movie) (anonymous)).same_directors(*(any args))
           expected: 1 time with any arguments
           received: 0 times with any arguments
     # ./spec/controllers/movies_controller_spec.rb:35:in `block (3 levels) in <top (required)>'

因此,您可以看到,当我使用间谍时,没有检测到对 :find:same_directors 的调用。

我无法在rspec.inforelishapp 中得到答案。如果您能提出一些建议,我将不胜感激。谢谢!

如果您需要,这就是控制器:

  def directors
    @movie = Movie.find(params[:id])
    @movies = @movie.same_directors
    if (@movies-[@movie]).empty?
      flash[:notice] = "'#{@movie.title}' has no director info"
      redirect_to movies_path
    end
  end

【问题讨论】:

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


    【解决方案1】:

    这是一个相关的问题:除了测试其行为之外,您可能还想确保调用某些控制器方法。例如,如果这些方法来自遗留代码,您希望通过自检测试确保其他人不会来并在控制器中替换她自己的方法。 (无论如何,谢谢你,感谢你的时间)。

    这是回答问题的代码:

    before :each do
        @movie1 = spy('Movie')
        allow(Movie).to receive(:find).and_return @movie1
        @any_possible_id = 1
        get :directors, :id=>@any_possible_id
    end
    it 'should call the model method that finds the movie corresponding to the passed id.' do
        expect(Movie).to have_received(:find).with(@any_possible_id.to_s)
    end
    it 'should call the model method that finds the movies corresponding to the same director.' do
        expect(@movie1).to have_received(:same_directors)
    end
    

    说明

    第 2 行 - @movie1 定义为类 Movie 之外的 spy。作为间谍,我们稍后可以在expect(@movie1).to have_received(:anything) 中使用它。如果@movie1 被定义为double,那么它becomes necessary to use 是一个:allow 子句,以允许它接收anything 方法(或“消息”,作为文档的说法)。

    第 3 行 - 这一行是解释 RSpec 抛出的错误的关键.and_return @movie1 是告诉 RSpec @movie1 在控制器中扮演 @movie 的角色的基础(参见问题末尾的控制器代码)。 RSpec 通过看到控制器中的Movie.find 返回@movie 来建立这种匹配,正如allow 语句中所指定的那样。问题中错误的原因不是Movie@movie没有在控制器中接收到指定的方法;原因是 RSpec 没有将规范中的 Movie@movie1 与控制器中的真实 Movie@movie 匹配。

    其余的不言自明。顺便说一句,我用其他方法来编写这个测试,但是使用 spy 的这个是最紧凑的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 1970-01-01
      • 2020-06-22
      相关资源
      最近更新 更多