I am running Rails 4 with Ruby 2.1.1.
I have in my Controller
def home
render :index
end
and I am writing a Unit::Test to check that the controller renders the view index with the method home.
How would I write the assertion? So far I have
test "home should have index" do
assert true
end
My test passes, of course, because I have assert true, but upon looking over the documentation, and having tinkered around, I am still unsure how to write a proper functional test for a Controller. Many sources are incomplete or vague, and seeing how no simple Stack Overflow question regarding writing a simple functional test using Unit::Test appears on Stack Overflow, I figured this would be a great question.
All I want is a clear answer to assert that my functional test checks that the home method renders index. Once I figure out how a basic functional test is written, I think I'll be able to proceed to testing the rest of my app.
Cheers
TL;DR I don't know how to write a basic functional test. How do I do that? Please be Unit::Test beginner friendly.
Use assert_template to assert that the request was rendered with the appropriate template file or partials.
test "home should have index" do
get :home
assert_template 'index'
end
Adding to infused's answer:
That is considered a functional test for a specific controller only, whose features and limitations are described in the RailsGuide here.
If your controller's name is StaticPagesController, the test should be in the file:
test/controllers/static_pages_controller_test.rb
The RailsGuide describes how to test for a rendered view here.
You can run the tests like this:
$ bundle exec rake test
or:
$ bundle exec rake test:functionals
seeing how no simple Stack Overflow question regarding writing a
simple functional test using Unit::Test appears on Stack Overflow
That's because Rails has its own testing framework. The file test/controllers/static_pages_controller_test.rb actually looks like this:
require 'test_helper'
class StaticPagesControllerTest < ActionController::TestCase
test 'home should get index' do
get :home
assert_template :index
end
end
Note the superclass: ActionController::TestCase. As the RailsGuide notes, ActionController::TestCase includes MiniTest, which is mostly a drop in replacement for Test::Unit.
Related
I an trying to organize code by making partial html.erb files that are shared frequently(e.g. _form.html.erb)
I want to check whether my partial code works well with different models/controllers, so I am manually doing CRUD from the views.
It would be nicer to test my code automatically using Rspec but I have no idea. Can anyone give me some guidance how to test controller code with Rspec?
To test controller and views together you write feature specs and request specs .
Request specs are lower level specs where you send HTTP requests to your application and write expectations (aka assertions in TDD lingo) about the response. They are a wrapper around ActionDispatch::IntegrationTest. Request specs should be considered the replacement for controller specs, the use of which are discouraged by by the RSpec and Rails teams.
# spec/requests/products_spec.rb
require 'rails_helper'
RSpec.describe "Products", type: :request do
describe "GET /products" do
let!(:products) { FactoryBot.create_list(:product, 4) }
it "contains the product names" do
get "/products"
expect(response).to include products.first.name
expect(response).to include products.last.name
end
end
end
Feature specs are higher level specs that focus on the user story. They often serve as acceptance tests. They use a browser simulator named Capybara which emulates a user clicking his way through the application. Capybara can also run headless browsers (headless chrome, firefox, phantom js, webkit etc) and "real" browsers through selenium. The minitest equivalent is ActionDispatch::SystemTestCase but RSpec features do not wrap it (it took minitest/testunit years to catch up here).
# Gemfile
gem 'capybara'
# spec/features/products_spec.rb
require 'rails_helper'
RSpec.feature "Products" do
let!(:products) { FactoryBot.create_list(:product, 4) }
scenario "when a user views a product" do
visit '/'
click_link 'Products'
click_link products.first.name
expect(page).to have_content products.first.name
expect(page).to have_content products.first.description
end
end
This specs tests the products#index and products#show action as well as the root page and the associated views.
Both types of specs have their strengths and weaknesses. Feature tests are good for testing large swaths of the application but are heavy. Request specs are faster and its easier to replicate a specific request that causes a bug/issue but you're basically just matching HTML with regular expressions which is highly limited.
To check whether partial code works well with different models/controllers. You can add render_views in controller specs.
How to test controller code with Rspec?
Read the official doc https://relishapp.com/rspec/rspec-rails/docs/controller-specs
And this page may help: https://thoughtbot.com/blog/how-we-test-rails-applications
I'm learning Ruby.
I'm now trying to test one of my controller.
My test file is myapp/test/testing.rb while my controller is located at myapp/app/controllers/test_controller.rb.
The content of testing.rb is
data = testController.mymethod()
puts(data)
But when doing
ruby myapp/test/testing.rb
in the terminal, I get a warning :
Traceback (most recent call last):myapp/test/testing.rb:6:in `':
uninitialized constant testController (NameError)
Could someone explain me what i'm doing is wrong and how I should do this ?
Thanks !
The currently accepted way to test rails controllers is by sending http requests to your application and writing assertions about the response.
Rails has ActionDispatch::IntegrationTest which provides integration tests for Minitest which is the Ruby standard library testing framework. Using the older approach of mocking out the whole framework with ActionDispatch::ControllerTest is no longer recommended for new applications.
This is your basic "Hello World" example of controller testing:
require 'test_helper'
class BlogFlowTest < ActionDispatch::IntegrationTest
test "can see the welcome page" do
get "/"
assert_select "h1", "Welcome#index"
end
end
You can also use RSpec which is different test framework with a large following. In RSpec you write request specs which are just a thin wrapper on top of ActionDispatch::IntegrationTest.
RSpec is DSL written on Ruby special for this purpose. You have to be familiar with it to write tests of your code. As for RSpec team note:
`
The official recommendation of the Rails team and the RSpec core team is to write request specs instead. Request specs allow you to focus on a single controller action, but unlike controller tests involve the router, the middleware stack, and both rack requests and responses. This adds realism to the test that you are writing, and helps avoid many of the issues that are common in controller specs. In Rails 5, request specs are significantly faster than either request or controller specs were in rails 4.
So you have to write requestspecs for your controller. Something like this:
spec/controllers/users_controller_spec.rb
RSpec.describe "Test", type: :request do
describe "request list of all tests" do
user = User.create(email: 'test#user.com', name: 'Test User')
get users_path
expect(response).to be_successful
expect(response.body).to include("Test User")
end
end
Something like this. Hope it will help
I have a weird issue when creating a controller spec for a namespaced controller when there also is a global controller with the same name.
The controller names are HomeController and Backend::HomeController.
Note that I have not yet created the file app/controllers/backend/home_controller.rb, only the global controller exists app/controllers/home_controller.rb
Therefore I expect the test to explode with errors, but it does not. It passes, all green and happy.
My spec looks like this
#spec/controllers/backend/home_controller_spec.rb
require 'rails_helper'
RSpec.describe Backend::HomeController, type: :controller do
before do
user = FactoryGirl.create(:user)
allow(controller).to receive(:authenticate_user!).and_return(true)
allow(controller).to receive(:current_user).and_return(user)
end
describe "GET #index" do
it "returns http success" do
get :index
expect(response).to have_http_status(:success)
end
end
end
However if I change the name in my global HomeController to something else, e.g NotMyHomeController the test fails with errors saying
Unable to autoload constant HomeController, expected app/controllers/home_controller.rb to define it
Which makes me suspect that Rspec doesn't bother with the "Backend" part in the Rspec.describe function.
Am I doing something wrong or am I missing some other vital part? IMHO, this spec shouldn't pass (to paraphrase Gandalf).
I'm using Rails 4.2.6, Rspec-Rails 3.4.2
Update
As Max pointed out, this is probably not at Rspec issue but instead something with Rails autoloading.
However i tried just typing
Backend::HomeController
In the Rails console, but there I get the expected error
NameError: uninitialized constant Backend::HomeController
And according to the Rails guide, both the console and the test suite autoloads. However I think I'm on the right track here.
I had this same problem before - you can read about the whole issue here:
Object.const_get and Rails - cutting off parent module names
but the meat of the answer comes from this answer from user Apneadiving:
Be aware that there are vicious cases in Rails development mode. In order to gain speed, the strict minimum is loaded. Then Rails looks for classes definitions when needed.
But this sometimes fails big time example, when you have say ::User already loaded, and then look for ::Admin::User. Rails would not look for it, it will think ::User does the trick.
This can be solved using require_dependency statements in your code.
I've been reading a ton of docs and SO questions/ answers on all the changes as Rspec has evolved, want to be sure of the answer...
My goal is to use native Rspec-rails (I have 3.2.2) to do integrated controller/view tests that look for 1) CSS classes and 2) ID selectors. In other words given this view snippet:
<!-- staticpages/dashboard -->
<div class="hidden">Something</div>
<div id="creation">This</div>
This should pass (however it should be semantically written):
describe StaticpagesController do
render_views
it "should find everything" do
get :dashboard
expect(response.body).to have_selector("div#creation")
expect(response.body).to have_css("hidden")
expect(response.body).to_not have_selector("div#nothinghere")
end
end
I would like to do this without additional gems like Capybara; is that possible?
Here's a high level of what I've learned so far:
in Rspec 1, the have_tag feature allowed you to do this (http://glenngillen.com/thoughts/using-rspec-have-tag)
in Rspec 2, the have_tag was replaced with webrat's have_selector (have_tag vs. have_selector)
in Rspec 3, webrat support has been removed (http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/)
In my own experimentation, the code above generated:
Expect<long response.body here>.to respond to `has_selector?`
So that has indeed been deprecated. Still, I'd love to know if there's some other way to do this that I don't know about.
IF it turns out I need Capybara to do these fancy matchers, is there a way to do this in my integrated controller/view specs? My understanding is that I have to add type: :feature to the describe StaticpagesController line to use Capybara's matchers. However, the minute I do that, render_views is no longer available (since it's limited to type: :controller). Note, render_views also dies if, per this post (https://www.relishapp.com/rspec/rspec-rails/v/2-99/docs/controller-specs/use-of-capybara-in-controller-specs), I manually include Capybara::DSL into my controller spec. Anyway, I would really like to not have to rewrite my current controller specs into a bunch of feature specs...
It would seem that you want feature specs (with Capybara) more than controller specs as you're not testing any of the things controller specs are typically used to test such as:
whether a template is rendered
whether a redirect occurs
what instance variables are assigned in the controller to be shared with the view
the cookies sent back with the response
Also, you probably want to consider writing feature specs for new apps over controller specs since controller tests will probably be dropped in Rails 5 in favor of the writing of integration/feature tests.
For a description of the different kinds of specs that you could write, and what they're typically used for,
see this SO answer.
I'm using rr (the mocking framework) and rspec with ruby-on-rails. Also, I'm using the collection short hand for partial rendering. My question: How do I correctly fill out the the following view spec?
describe 'my_view' do
before(:each) do
assigns[:models] = Array.new(10, stub(Model))
end
it "should render the 'listing' partial for each model" do
# help me write something that actually verifies this
end
end
I've tried a few examples from the rspec book, rspec docs, and rr docs. Everything I try seems to leave me with runtime errors in the test - not failed assertions. Rather than show all the transformations I've tried, I figured all I'd need if someone showed me one that actually worked. I'd be good to go from there.
I would suggest asserting the presence of some HTML that the "listing" partial should generate. Otherwise, it sounds like you're trying to assert that Rails is technically calling render on the partial? That's the job of the Rails core tests to prove such functionality.