简体   繁体   中英

How to write RSpec test for the following code

I have the functionality of my code working correctly, but need to create an RSpec test that covers it. My routes.rb:

  resources :movies do
    #member routes for individual ones
    get 'find_with_same_director', on: :member
  end
  # map '/' to be a redirect to '/movies'
  root :to => 'movies#index'

My code in movies_controller.rb:

def find_with_same_director
  @movie = Movie.find(params[:id])
  @movies, check_info = Movie.find_with_same_director(params[:id])
  if check_info
    flash[:notice] = "'#{@movie.title}' has no director info"
    redirect_to movies_path
  end
end

The find_with_same_director function in the model, movie.rb:

def self.find_with_same_director(id)
  movie = self.find(id)
  if !movie.director.blank?
    movies = self.where(:director => movie.director).where.not(:id => movie.id)
    return movies, false
  else
    return [], true
  end
end

I am trying to write tests that covers the click on the "Find With Same Director" link that calls the function, when the movie that was clicked has a director to show, and when it doesn't. I've written the following tests in movies_controller_spec.rb for each so far:

describe 'find_with_same_director' do
  it 'should call the find_with_same_director model method' do
    expect(Movie).to receive(:find_with_same_director).with(params[:id])
    get :find_with_same_director, id: movie.id
  end

  context 'movie has a director' do
    let!(:movie1) {FactoryGirl.create(:movie, :director => movie.director)}
    it do
      get :find_with_same_director, id: movie1.id
      expect(response).to redirect_to(movie_path(movie1.id))
    end
  end

  context 'movie has no director' do
    movie1 = FactoryGirl.create(:movie, :director => nil)
    it "should redirect to root" do
      get :find_with_same_director, id: movie1.id
      expect(response).to redirect_to(/movies)
    end
  end
end

I have spent hours working on these tests and while they are "covering" the lines when i check the report, the first two return fails. This means I wrote them incorrectly. I want to modify these tests to accurately represent what my controller code is doing and I would really appreciate some help with that. If you're feeling down to it I would also appreciate if you gave advice on writing the rspec test code for the model movie.rb file as well.

The error I get when I isolate the first test:

  1) MoviesController find_with_same_director should call the find_with_same_director model method
     Failure/Error: expect(Movie).to receive(:find_with_same_director).with(params[:id])

     NameError:
       undefined local variable or method `params' for #<RSpec::ExampleGroups::MoviesController::FindWithSameDirector:0x000000056b27e0>

The error I get when I isolate the second test:

Failures:

  1) MoviesController find_with_same_director movie has a director should redirect to "/movies/28"
     Failure/Error: expect(response).to redirect_to(movie_path(movie2.id))
       Expected response to be a <redirect>, but was <200>

I kind of understand why the errors are happening, I just don't know a fix for them.

Tests usually run in isolation. Therefore let's look at them one after the other:

Your first spec looks like this:

it 'should call the find_with_same_director model method' do
  expect(Movie).to receive(:find_with_same_director).with(params[:id])
  get :find_with_same_director, id: movie.id
end

It is important to note that in the context of this test either params nor movie do exist because you did define them first. You might want to fix this by creating a movie first:

let(:movie) { FactoryGirl.create(:movie) }

it 'should call the find_with_same_director model method' do
  expect(Movie).to receive(:find_with_same_director).with(movie.id)
  get :find_with_same_director, id: movie.id
end

Two suggestion:

  1. FactoryGirl is deprecated for a good reason . It was replaced by FactoryBot . Please update it.
  2. IMO this spec should not exist at all, because it tests an internal implementation detail. Tests should specific what a method returns or what side effects the method has. But Test should not test how something is done. The reason is very simple. Such tests would break when you refactor that method even if the method still returns the exact seems response.

Your second spec:

context 'movie has a director' do
  let!(:movie1) { FactoryGirl.create(:movie, :director => movie.director) }

  it do
    get :find_with_same_director, id: movie1.id
    expect(response).to redirect_to(movie_path(movie1.id))
  end
end

This spec has two issues. It seems like you assume to find a similar movie and redirect to that movie. But you only create only one movie, there is no other movie to redirect to. And even if it was existing there is no redirect in your controller and because you method returns multiple movies it is unclear which similar movies to redirect to.

Your third spec doesn't create a movie correctly. Once that is fixed, I think the spec will pass.

context 'movie has no director' do
  let(:movie) { FactoryGirl.create(:movie, :director => nil) } # <= this creates the movie for the test

  it "should redirect to root" do
    get :find_with_same_director, id: movie.id
    expect(response).to redirect_to(/movies)
  end
end

Furthermore, I suggest replacing the find_with_same_director with a scope and have a look into the documentation of Rspec – especially how controller specs are working and the difference between let and let! .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM