Introduction
Rails continues to refine its built-in error handling tools. Testing error reporting behavior has become more precise with new testing utilities introduced in Rails 7.1.
Rails 7.1 brought us assert_error_reported and assert_no_error_reported.
These additions make our test suites more expressive and align better with Rails testing conventions.
Rails 8.1 takes this further with capture_error_reports.
This latest addition gives us even more control when we need to verify error details like messages and context.
Before
Before this change, verifying that an error was reported through Rails.error.report was not straightforward.
We often relied on mocks or indirect test expectations. This approach ensured that our code handled errors properly.
# Old approach using mocks
expect(Rails.error).to receive(:report).with(instance_of(MyCustomError))
MyService.callWhile this worked, it had drawbacks. It tightly coupled our tests to internal Rails APIs. This made tests harder to maintain over time.
After
With the latest Rails improvements, we can use built-in assertions. We can now check whether an error has been reported without relying on mocks.
# New approach with built-in assertions
def test_error_reporting
assert_error_reported(MyCustomError) do
MyService.call
end
end
def test_no_error_reporting
assert_no_error_reported do
SafeService.call
end
endThese assertions automatically track calls to Rails.error.report.
They work within the provided block and ensure our expectations are met.
This helps us write cleaner, more reliable tests.
How It Works
These assertions work seamlessly with Rails.error.
They integrate with ActiveSupport::Testing::Assertions.
Matching specific errors: We can match specific error classes or conditions.
Clear failure messages: The assertions provide clear failure messages. This happens when an error is reported (or not reported) unexpectedly.
Declarative test cases: They encourage us to write more declarative and readable test cases.
By using these new assertions, we improve our test suites. Both readability and confidence increase significantly.
Capturing Error Reports for Detailed Verification
Sometimes we need more than just asserting that an error was reported. We might want to verify error messages, context, or other metadata.
Rails 8.1 introduces capture_error_reports for these scenarios.
This method captures all errors reported within a block and returns them as an array.
It’s a natural evolution of the error reporting test utilities that started in Rails 7.1.
def test_error_details
reports = capture_error_reports do
Rails.error.report(IOError.new("Something went wrong"), context: { user_id: 123 })
end
assert_equal 1, reports.size
assert_equal "Something went wrong", reports.first.error.message
assert_equal 123, reports.first.context[:user_id]
endThis is particularly useful when we need to:
Verify error messages: Check that the right error message is being reported.
Inspect error context: Ensure proper context data is attached to error reports.
Filter specific error types: Capture only errors of a specific class while ignoring others.
def test_filtered_errors
reports = capture_error_reports(IOError) do
Rails.error.report(IOError.new("Oops"))
Rails.error.report(IOError.new("Oh no"))
Rails.error.report(StandardError.new)
end
assert_equal 2, reports.size
assert_equal "Oops", reports.first.error.message
assert_equal "Oh no", reports.last.error.message
endThe capture_error_reports method gives us fine-grained control over our error reporting tests.
It complements the assertion methods perfectly.
Why This Matters
Error handling is an essential part of any robust Rails application. These new assertions make it easier to ensure errors are captured and reported consistently.
Benefits:
They reduce boilerplate code in our tests.
They eliminate the need for mocking Rails internals.
They make our test suite’s intent much clearer.
This aligns perfectly with Rails’ philosophy of convention over configuration. Rails continues to provide expressive and intuitive APIs for common needs.
