How do I test for an exception in minitest? - unit-testing

I'm using Rails 5 with minitest. How do I properly catch an exception being raised from invoking a controller method? I thought
assert_raises
was the key. However, I have this method
def show
#line = Line.find(params[:id])
end
and although I thought I'm capturing a REcordNotFoundError but writing
# Simple test to verify we get the show page when we
# invoke the page with a valid ID
test "get show page with invalid line id" do
invalid_line_id = -1
assert_raises ActiveRecord::RecordNotFound get line_path(invalid_line_id)
end
and then running it results in a
# Running:
.E
Error:
LinesControllerTest#test_get_show_page_with_invalid_line_id:
ActiveRecord::RecordNotFound: Couldn't find Line with 'onestop_id'=-1
app/controllers/lines_controller.rb:7:in `show'
test/controllers/lines_controller_test.rb:31:in `block in <class:LinesControllerTest>'
error. What's the right way to catch the error in my test?

assert_raises expects a block - you've just called the code.
Try something like:
assert_raises ActiveRecord::RecordNotFound do
get line_path(invalid_line_id)
end

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!

Capybara: Check children of a div having class

I am trying to check if a div has a child with particular class in Capybara, using the following piece of code:
expect(find("#admin-row-1 .glyphicon-ban-circle")).to_not be_empty
Upon debug, I get the following output
(byebug) find("#admin-row-1 .glyphicon-ban-circle")
#<Capybara::Node::Element tag="a" path="/html/body/main/div[2]/div/div/div/table/tbody/tr[3]/td[3]/a[2]">
But still, getting the following expectation error
Failure/Error: expect(find("#admin-row-1 .glyphicon-ban-circle")).to_not be_empty
expected #<Capybara::Node::Element tag="a" path="/html/body/main/div[2]/div/div/div/table/tbody/tr[3]/td[3]/a[2]"> to respond to `empty?`
find returns an element or raises an exception, it doesn't return anything tha responds to empty?, You could use all instead which returns an array like object but a better solution is to use the have_css matcher provided by Capybara
expect(page).to have_css('#admin-row-1 .glyphicon-ban-circle')

Rails 4 - Restful index action for for both get and post

I made 'search' action available for both get and post
resources :wba_partners do
collection do
match "wba_partners/search" => "wba_partner#search", via: [:get, :post]
end
end
for POST method it works as it should be '/wba_partners/search'.
for Get method it is throwing error:
ActiveRecord::RecordNotFound in WbaPartnersController#show
Couldn't find WbaPartner with 'id'=search
Any idea how it can be solved? Thanks.

Stubbed method can't be found in rspec test

In my controller I have code like...
def apply
...do some validation stuff
jobapp = JobApplication.new
jobapp.apply_for_job(params, job)
end
In my test I want to make sure that after all the validation passes, that the apply_for_job method was called so I have the following test.
describe 'apply' do
before(:each) do
#file = Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/files/test-resume.txt'), 'plain/text')
allow_any_instance_of(JobApplication).to receive(:apply_for_job).and_return(true)
end
it 'assuming all validation passes, it calls the jobapplication apply_for_job method' do
post :apply, file: #file, job_id: 1, format: :json
expect_any_instance_of(JobApplication).to receive(:apply_for_job)
end
end
When I run my test I get this error.
Failure/Error: Unable to find matching line from backtrace
Exactly one instance should have received the following message(s) but didn't: apply_for_job
Any ideas why? Thanks.
expect_any_instance sets the expectation at the time that line of code is executed. It does not verify an expectation on a spy.
You are setting the expectation after your code under test has run. Move the expectation definition up one line:
expect_any_instance_of(JobApplication).to receive(:apply_for_job)
post :apply, file: #file, job_id: 1, format: :json

No route matches with valid object

Having read this:
"No route matches" error?
I'm trying to figure out if there is a gem or way to monkey patch actionpack to get around this constraint.
Basically, I'm writing specs (they run fast), and I don't understand why actionpack throws this error when being applied to an object which isn't "saved".
For two reasons:
Why is it throwing a "No route matches" when it really should be throwing something more meaningful (e.g. object must be saved before a route can be constructed, or object ID is nil). The exception seems a little obscure.
I shouldn't have to save the object at all if all I am trying to do is generate a url for that object, given the ID is populated using a factory or something similar.
This constraint makes it a pain to write fast tests, unless I'm missing something...
True, the error message is a bit obscure. Regarding your second point, you don't need to save an object to generate a URL, the helper will work just as well with a literal value.
building_path(1) # GET /buildings/1 => BuildingsController#show, params={:id=>"1"}
So in the example the object can be replaced with any value:
get :show, :id => "1"
For example, if you use rails generate scaffold Article, RSpec will build a spec like this:
def mock_article(stubs={})
(#mock_article ||= mock_model(Article).as_null_object).tap do |article|
article.stub(stubs) unless stubs.empty?
end
end
describe "GET show" do
it "assigns the requested article as #article" do
Article.stub(:find).with("37") { mock_article }
get :show, :id => "37"
assigns(:article).should be(mock_article)
end
end
which does not hit the database.