Why are there 2 kinds of controller tests in MiniTest? - ruby-on-rails-4

I'm converting over to MiniTest from RSpec, and having a couple of difficulties doing so. I have been following some examples I have found:
class ArticlesControllerTest < ActionController::TestCase
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:articles)
end
end
So that's a class that inherits from ActionController::TestCase, that makes sense.
But then there are other examples like this:
require 'test_helper'
describe ThingsController do
describe "#create" do
it do "valid"
login_user
post :create, { catalog: { name: "My Thing", description: "Description of my thing."}}
assert_redirected_to thing_path(Thing.last)
end
end
end
Why are these two styles different? I'm using the second example, and none of my redirects are working like they do in my dev system. Trying to get to the bottom of it.

First one is Minitest::Unit test syntax explained here
Second is more like Rspec syntax, you can use minitest-spec-rails gem for that

Related

ruby rails test case failing but real app works

So here's a strange problem: When I start my local rails app and browse to http://localhost:3000/static_pages/help I can see the page I created there.
However, the test case that I wrote says otherwise.
static_pages_controller_test.rb
require 'test_helper'
class StaticPagesControllerTest < ActionController::TestCase
test "should get home" do
get :home
assert_response :success
end
test "should get help" do
puts static_pages_help_url
puts static_pages_help_path
get static_pages_help_url
assert_response :success
end
end
It fails with this error, Output of $bin/rake test:
Running:
..http://test.host/static_pages/help
/static_pages/help
E
Finished in 0.466745s, 8.5700 runs/s, 4.2850 assertions/s.
1) Error.
StaticPagesControllerTest#test_should_get_help:
ActionController::UrlGenerationError: No route matches {:action=>"http://test.host/static_pages/help", :controller=>"static_pages"}
test/controllers/static_pages_controller_test.rb:12:in `block in <class:StaticPagesControllerTest>'
Here is routes.rb
Rails.application.routes.draw do
get 'static_pages/home'
get "static_pages/help"
end
and here is the static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
end
and these two files
app/views/static_pages/home.html.erb
app/views/static_pages/help.html.erb
exist, as I can also see them when navigating to /static_pages/help in my browser. I've searched the web for hours, no clue.
$ rails --version
Rails 4.2.7.1
$ ruby --version
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
I must be missing something. Please help.
Since you're writing a controller spec, the parameter to a GET should be the action(controller method). But you're passing a URL. If you look at the error message, you can find that "http://test.host/static_pages/help" was passed into action. So, pass the name of the controller method as a symbol rather than the URL. Try
get :help
Note that help is the controller action.
However if you're interested in writing an integration test, you should inherit from ActionDispatch::IntegrationTest rather than ActionController::TestCase. So, your spec should look aomething like this.
class StaticPagesControllerTest < ActionDispatch::IntegrationTest
test "should get home" do
get static_pages_home_url
assert_response :success
end
test "should get help" do
get static_pages_help_url
assert_response :success
end
end
To learn more about integration and controller tests, see http://weblog.jamisbuck.org/2007/1/30/unit-vs-functional-vs-integration.html
Hope this helps!

undefined method 'document' for nil:NilClass while running Rails-tutorial with rspec

I am following the railstutorial by Michael Hartl, and I don't understand the reason for failing tests in Chapter 5. The book used the minitest framework but I decided to use RSpec. To do this, I deleted the test folder and included rspec-rails in my Gemfile then ran bundle install and rails g rspec:install to generate my spec folders. However, there are some tests that I feel convenient running with minitest syntax such as assert_select in static_pages_controller_spec.rb file. Here is how my spec file looks like:
require "rails_helper"
RSpec.describe StaticPagesController, type: :controller do
describe "GET #home" do
it "returns http success" do
get :home
expect(response).to have_http_status(:success)
end
it "should have the right title" do
get :home
assert_select "title", "Ruby on Rails Tutorial Sample App"
end
end
describe "GET #help" do
it "returns http success" do
get :help
expect(response).to have_http_status(:success)
end
it "should have the right title" do
get :help
assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
end
end
describe "GET #about" do
it "returns http success" do
get :about
expect(response).to have_http_status(:success)
end
it "should have the right title" do
get "about"
assert_select "title", "About | Ruby on Rails Tutorial Sample App"
end
end
end
When I run the tests with RSpec, this is what I get as the failure error:
StaticPagesController GET #home should have the right title
Failure/Error: assert_select "title", "Ruby on Rails Tutorial Sample App"
NoMethodError:
undefined method `document' for nil:NilClass
# ./spec/controllers/static_pages_controller_spec.rb:11:in `block (3 levels)
in <top (required)>'
The same error message (No Method error) appears in each of the failing tests.
How can I fix it? Is there something I am doing wrong.
The reason for this error is that RSpec doesn't render views for controller specs by default. You can enable view rendering for a particular group of specs like this:
describe FooController, type: :controller do
render_views
# write your specs
end
or you can enable it globally by adding this somewhere in your RSpec config:
RSpec.configure do |config|
config.render_views
end
See https://www.relishapp.com/rspec/rspec-rails/v/2-6/docs/controller-specs/render-views for more information.
The problem is that assert_selected is a MiniTest construct whereas you are using RSpec. You will need to use RSpec mechanisms for expecting view content https://relishapp.com/rspec/rspec-rails/v/3-4/docs/view-specs/view-spec or add capybara to your Gemfile and use the capybara matchers: https://gist.github.com/them0nk/2166525

"undefined method `env' for nil:NilClass" in 'setup_controller_for_warden' error when testing Devise using Rspec

I'm trying to create a spec for a sign out flow by using factorygirl to create a user and then use Devise's sign_in method to authenticate the user, then use capybara to click the "Sign Out" link.
I'm getting (what seems to me to be) a strange error when I run the spec:
Failures:
1) Sign out flow successfully redirects to the welcome index (root)
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `env' for nil:NilClass
# /home/vagrant/.rvm/gems/ruby-2.0.0-p576/gems/devise-3.4.1/lib/devise/test_helpers.rb:24:in `setup_controller_for_warden'
Finished in 0.00226 seconds (files took 3.32 seconds to load)
1 example, 1 failure
Here's the spec:
require 'rails_helper'
describe "Sign out flow" do
include Devise::TestHelpers
describe "successfully" do
it "redirects to the welcome index (root)" do
user = create(:user)
sign_in user
within '.user-info' do
click_link 'Sign Out'
end
expect(current_path).to eq root_path
end
end
end
And my user.rb factory:
FactoryGirl.define do
factory :user do
name "Fake User"
sequence(:email, 100) { |n| "person#{n}#example.com" }
password "helloworld"
password_confirmation "helloworld"
confirmed_at Time.now
end
end
The error seems to be triggered simply from the line include Devise::TestHelpers, as I've tried commenting out the entire content of the spec and still get the same error.
I thought the Devise test helpers would work out of the box; did I miss some configuration? Thanks.
In Rails 5 you must include Devise::Test::IntegrationHelpers instead Devise::Test::ControllerHelpers:
# rails_helper.rb
config.include Devise::Test::IntegrationHelpers, type: :feature
See more:
https://github.com/plataformatec/devise/issues/3913#issuecomment
https://github.com/plataformatec/devise/pull/4071
Apparently there are issues with Devise::TestHelpers and integration testing, so perhaps that's the problem here.
https://github.com/plataformatec/devise (mentioned in README, Issues, etc.; also see related SO questions):
These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session;
FWIW it seems like the issues have been fixed, however I ran into the issue after not reading the documentation well enough.
This was our code:
RSpec.configure do |config|
...
config.include Devise::TestHelpers
...
end
This means every test will include the test helpers, including models. This wound up being the issue for us. Should we have read the documentation closer we would have noticed Devise suggests limiting it to only controllers with:
RSpec.configure do |config|
...
config.include Devise::TestHelpers, type: :controller
...
end
This solved the issue for us. All tests passing :)
Here's my solution:
class ActiveSupport::TestCase
# all the normal stuff
end
class ActionController::TestCase
include Devise::TestHelpers
end
I meet the same error on rails 5.
Here's my solution
spec/rails_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.include Devise::TestHelpers, type: :view
config.include Warden::Test::Helpers
end
spec/controllers/your_controller_spec.rb
RSpec.describe YourController, type: :controller do
before(:all) do
user = FactoryGirl.create(:user)
login_as user, scope: :user
end
it "#index" do
get "index"
expect(response).to render_template(:index)
expect(response).to have_http_status(200)
end
$ rspec --tag focus
Run options: include {:focus=>true}
DashboardController
#index
Finished in 3.9 seconds (files took 3.5 seconds to load)
1 example, 0 failures
Like others have already said, you're including the Devise::TestHelpers. That's for testing controllers. If you'd still like to automatically login a test user in your integration tests, check out the official Devise Instructions on using it with Capybara.
Using Devise with Capybara
Basically, what you need to do is first enable Warden's test mode:
include Warden::Test::Helpers
Warden.test_mode!
Then, (create and) login your user:
user = FactoryGirl.create(:user)
login_as(user, scope: :user)
Example:
# spec/features/survey_spec.rb
require 'rails_helper'
feature 'survey app' do
include Warden::Test::Helpers
let(:user) { create(:user) }
let(:survey) { create(:survey_with_questions) }
before do
# Sign the User in
Warden.test_mode!
login_as(user, scope: user)
end
it 'renders the survey' do
visit survey_show_path(survey)
expect(page).to have_content(survey.title)
end
end
I was having this problem when trying to sign_in a user in a before hook:
before(:context) do
create(:skill, name: 'Google Maps API'.downcase)
user = create(:user)
sign_in user
end
Placing sign_in inside the before hook leads to:
Failure/Error: sign_in user
NoMethodError:
undefined method `env' for nil:NilClass
But placing it inside an example works fine:
shared_examples_for('an authenticated resource.') do
describe 'An authenticated request' do
it "responds with HTTP status OK" do
user = create(:user)
sign_in user
make_request
expect(response).to have_http_status(:ok)
end
end
end
But this can be improved, placing the sign_in into a before(:example) that will also work:
context 'allow search by keyword' do
let!(:skill){ create(:skill, name: 'Google Maps API'.downcase) }
let!(:user) { create(:user) }
before(:example) { sign_in user }
it 'finds matching records' do
get :search, name: "Google Maps API", format: :json
expect(assigns(:skills).size).to be(1)
end
it 'finds records that start with keyword'
it 'finds records that end with keyword'
it 'finds records that contains keyword'
end
My Devise version is 4.2.0 so I just included
config.include Devise::Test::ControllerHelpers, type: :controller
in my rails helper file.
Alternatively you can use the same in your spec as
include Devise::Test::ControllerHelpers
The correct syntax for Rails 5 / Devise (4.2.0) is
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, :type => :controller
end
Devise::TestHelpers are deprecated so use Devise::Test::ControllerHelpers
:type => :controller - to limit only for
controllers and not models for example.
For the sake of being complete, with Rails 5 and RSpec I have run into similar issues when using the latest helpers, as they need to be set explicitly with the type when not used as a superclass.
So if you find yourself receiving there errors in your model tests there's a pretty good chance the type is not set.
Here's what I use in the spec_helper:
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :feature
I know that the docs do mention this, but there are times when you may run across an older blog that gives you an older approach, or upgrading from an older setup, and next thing you know this happens.

Rspec assigns equivalent in unit test/minitest

Is there rspec assigns equivalent in MiniTest::Test? If not how can I check the instant controller instant variable in tests. I am using sinatra
The assigns method comes from Rails, not RSpec or Minitest. It works just the same.
The following example is from the Rails Testing Guide:
class PostsControllerTest < ActionController::TestCase
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:posts)
end
end

creating mocks for controller method with Rspec in rails

Can someone help me create mocks for the following code. I want to add a controller method in existing controller by following name and want to test its behavior to a movie class containing title, director, rating etc as table instances. Unfortunately I am unfamiliar with BDD commands to use here.
describe MoviesController do
describe "#find_same_director" do
before :each do
fake_movies = [mock('movie1'), mock('movie2')]
end
context "with invalid attributes" do
it "flashes no such director message" do
flash[:notice].should_not be_nil
end
it "redirects to the index method" do
response.should redirect_to movies_path
end
end
context "with valid attributes" do
it "calls model method to find all movies" do
movie = Movie.find_with_director, {:director => 'George Lucas'}
get :show, id: #fake_movies
assigns(:movie).should eq(#fake_results)
end
it "renders the #find_same_director view" do
get :find_same_director, id: #fake_movies
response.should render_template :find_same_director
end
end
end
end
Are you noticing that you are trying to test different things at different test cases? (the first contexts you aren't executing the action "get :x", the last ones you are doing "get :show"
First of all you should think about the behavior of your code, so, i can think of two contexts (what kind of situations do you have in this case):
# with valid parameters(for e.g.: i should pass the right data, before this context i must create the data for the text).
# with invalid parameters(for e.g: the parameters passed to the GET request should not be existent on the system).
Then you should think about what happens when this contexts are active.
context "with valid parameters" do
it "should return the other movies of the same director, and assign it to the #movies"
it "should render the template of the #find_same_director page"
end
context "with invalid parameters" do
it "should redirect to the movies_path"
it "should put a flash message that the director is invalid"
end
After you think about the test cases do you have to think about how to implement them, i'll give you a hint:
it "should return the other movies of the same director, and assign it to the #movies" do
# THINKING ABOUT BDD HERE YOU SHOULD THINK OF THIS CODE SECTIONS AS FOLLOW:
# GIVEN ( OR THE CONDITIONS FOR THE ACTION HAPPEN)
#director = Director.new
movies = [Movie.new, Movie.new]
#director.movies = movies
# HERE ILL FIX THE VALUES SO I CAN USE IT ON MY EXPECTATIONS
Director.stub!(:find).with(#director_id).and_return(#director)
# WHEN, THE ACTION HAPPENED
get :find_same_director, :id => #director_id
# THEN THE EXPECTATIONS THAT SHOULD BE MATCHED
assigns(:movies).should == #director.movies
end
For a more real experience with tests i recommend you to watch the screencasts: http://destroyallsoftware.com/