使用 Spies 检测方法调用
Use of Spies for detecting method calls
我是 RSpec 的新手。我正在使用以下(正确的)规范代码来确保在测试的控制器中调用方法 find
和 same_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
子句来使其干燥。
当然,最好的解决办法是把它移到一个 after
子句中(我意识到当我写这个问题时,我做到了并且它起作用了。)但我现在想要的是理解为什么我使用此处的间谍无法正常工作。
我对 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.info nor in relishapp 中找到答案。如果您能提出建议,我将不胜感激。谢谢!
如果你需要它,这是控制器:
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
这是一个相关的问题:除了测试其行为外,您可能还想确保调用了某些控制器方法。例如,如果这些方法来自遗留代码,您希望通过自检测试确保其他人不会来替换控制器中她自己的方法。 (无论如何谢谢你,我感谢你的时间)。
这是回答问题的代码:
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
被定义为一个 spy out of the class Movie
. Being a spy, we can use it later in expect(@movie1).to have_received(:anything)
. If @movie1
is defined as a double
then it becomes necessary to use 一个 :allow
子句,以允许它接收方法 anything
(或 "message",作为文档的说法)。
第 3 行 - 这一行是解释 RSpec 抛出的错误 的关键。 .and_return @movie1
是告诉 RSpec @movie1
在控制器中扮演 @movie
角色的基础(请参阅问题末尾的控制器代码)。 RSpec 通过在控制器 returns @movie
中看到 Movie.find
来建立此匹配,如此 allow
语句中所指定。问题中错误的原因不是 Movie
或 @movie
没有在控制器中接收到指定的方法;原因是 RSpec 与规范中的 Movie
和 @movie1
与控制器中的真实 Movie
和 @movie
不匹配。
其余部分不言自明。顺便说一句,我用其他方式编写这个测试,但是这个使用间谍的方式是最紧凑的。
我是 RSpec 的新手。我正在使用以下(正确的)规范代码来确保在测试的控制器中调用方法 find
和 same_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
子句来使其干燥。
当然,最好的解决办法是把它移到一个 after
子句中(我意识到当我写这个问题时,我做到了并且它起作用了。)但我现在想要的是理解为什么我使用此处的间谍无法正常工作。
我对 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.info nor in relishapp 中找到答案。如果您能提出建议,我将不胜感激。谢谢!
如果你需要它,这是控制器:
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
这是一个相关的问题:除了测试其行为外,您可能还想确保调用了某些控制器方法。例如,如果这些方法来自遗留代码,您希望通过自检测试确保其他人不会来替换控制器中她自己的方法。 (无论如何谢谢你,我感谢你的时间)。
这是回答问题的代码:
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
被定义为一个 spy out of the class Movie
. Being a spy, we can use it later in expect(@movie1).to have_received(:anything)
. If @movie1
is defined as a double
then it becomes necessary to use 一个 :allow
子句,以允许它接收方法 anything
(或 "message",作为文档的说法)。
第 3 行 - 这一行是解释 RSpec 抛出的错误 的关键。 .and_return @movie1
是告诉 RSpec @movie1
在控制器中扮演 @movie
角色的基础(请参阅问题末尾的控制器代码)。 RSpec 通过在控制器 returns @movie
中看到 Movie.find
来建立此匹配,如此 allow
语句中所指定。问题中错误的原因不是 Movie
或 @movie
没有在控制器中接收到指定的方法;原因是 RSpec 与规范中的 Movie
和 @movie1
与控制器中的真实 Movie
和 @movie
不匹配。
其余部分不言自明。顺便说一句,我用其他方式编写这个测试,但是这个使用间谍的方式是最紧凑的。