What is the expected syntax for checking exception messages in MiniTest's assert_raises/must_raise?
You can use the assert_raises
assertion, or the must_raise
expectation.
it "must raise" do
assert_raises RuntimeError do
bar.do_it
end
-> { bar.do_it }.must_raise RuntimeError
lambda { bar.do_it }.must_raise RuntimeError
proc { bar.do_it }.must_raise RuntimeError
end
If you need to test something on the error object, you can get it from the assertion or expectation like so:
describe "testing the error object" do
it "as an assertion" do
err = assert_raises RuntimeError { bar.do_it }
assert_match /Foo/, err.message
end
it "as an exception" do
err = ->{ bar.do_it }.must_raise RuntimeError
err.message.must_match /Foo/
end
end
Why doesn't Minitest's assert_raises work as expected in this case?
This isn't a problem with assert_raises
, that continues to work just fine. The issue you are having is that the exception you are raising within your Rails app is getting handled by your Rails app and not propagating out to your test. Calling get '/missing'
is raising the AbstractController::ActionNotFound
error, but then your app handles the error and returns an appropriate response (404 or 500) to the client (your test).
Okay, so how would you go about testing that your controller really did raise the error you are expecting? Normally you would just use an ActionController::TestCase
test to test your controllers. But, when you call an action on a controller that doesn't in ActionController::TestCase
you get a different error. So the following:
require "test_helper"
class TestControllerTest < ActionController::TestCase
def test_missing
assert_raises AbstractController::ActionNotFound do
get :missing
end
end
end
Produces the following output:
1) Failure:
TestControllerTest#test_missing [test/controllers/test_controller_test.rb:6]:
[AbstractController::ActionNotFound] exception expected, not
Class: <ActionController::UrlGenerationError>
Message: <"No route matches {:action=>\"missing\", :controller=>\"test\"}">
---Backtrace---
test/controllers/test_controller_test.rb:7:in `block in test_missing'
test/controllers/test_controller_test.rb:6:in `test_missing'
---------------
The reason is because ActionController::TestCase
is aware of the routes and won't allow calling an action that is not in the routes. But that is exactly what you are trying to test. And, I assume why you aren't using ActionController::TestCase
.
At this point I wonder if you aren't testing something you shouldn't. Perhaps you should allow Rails to do its job and trust that it behaves correctly. But hey, we've come this far, why not just go all the way and test what Rails tests anyway. Instead of making the call through the full Rails app, let's try calling the TestController
directly. If we create a new instance of the controller we can ask it to handle an action even if that action is not defined on the controller. But to do this we will have to dive deep into how ActionController works and make use of the AbstractController#process
method:
require "test_helper"
class TestControllerTest < Minitest::Test
def test_missing
assert_raises AbstractController::ActionNotFound do
TestController.new.process :missing
end
end
end
Now we are asking the controller to process an action that doesn't exist and testing that the controller behaves as expected. Yay?
Rails ActiveSupport: How to assert that an error is raised?
So unit testing isn't really in activesupport. Ruby comes with a typical xunit framework in the standard libs (Test::Unit in ruby 1.8.x, MiniTest in ruby 1.9), and the stuff in activesupport just adds some stuff to it.
If you are using Test::Unit/MiniTest
assert_raise(Exception) { whatever.merge }
if you are using rspec (unfortunately poorly documented, but way more popular)
lambda { whatever.merge }.should raise_error
If you want to check the raised Exception
:
exception = assert_raises(Exception) { whatever.merge }
assert_equal( "message", exception.message )
error message test on minitest
raise Type_Error, 'First Name must not be empty' if @person.first_name == nil
For testing above line, I wrote a test like below. I used minitest::spec for this.
def test_first_name_wont_be_nil
person.name = nil
exception = proc{ Context.new(person).call }.must_raise(TypeError)
exception.message.must_equal 'First Name must not be empty'
end
Context is place where make some process.
NoMethodError: undefined method `RuntimeError' when I use {...} syntax instead of do...end with minitest
There is a significant difference between do..end
and {...}
: the precedence matters. Curlies are greedy, do...end
are not.
Contrived example:
puts [1].each { |i| i } #⇒ 1
puts [1].each do |i| i end #⇒ #<Enumerator:0x000000067dd678>
That said, to make curlies to work as expected, one should use parentheses:
assert_raises(RuntimeError) { @formatter.output_report }
Related Topics
Detect Number of Cpus Installed
How to Avoid Duplicates in a Has_Many :Through Relationship
Ruby on Rails Gem for Google Map Integration
How to Remove All Non - Ascii Characters from a String in Ruby
Is a System-Wide Install of Rvm a Bad Idea
How to I Make Private Class Constants in Ruby
How to List All Files in an S3 Folder Using Aws-Sdk Gem in Ruby on Rails
How to Add Custom Filter to Active Admin
Ruby: How to Iterate Over an Array of Hashes and Return the Values in a Single String
Creating Signature and Nonce for Oauth (Ruby)
Retrieving Array of Ids in Mongoid
Error Installing Debugger-Linecache in Ruby 1.9.3
Find Most Common String in an Array
Ruby: Most Concise Way to Use an Env Variable If It Exists, Otherwise Use Default Value