Happy Thanksgiving!
I am testing an application that reports all errors through a common
interface, which then distributes errors to various loggers (rails logs,
logentries, etc). Each reporter logs messages based on severity and
environment. In our test environment, the generic logging interface is set
to always re-raise reported errors such that tests will always fail if an
error is reported through this interface that is not expected. In the
majority of cases, this means that conditions where such errors would
normally be reported, handled and swallowed in production will instead
re-raise the exception (in test) and ultimately cause the test to fail if
the error is not explicitly expected.
However, we still observe conditions where:
1. class A calls class B calls class C.
2. class C raises an exception.
3. class B catches said exception and logs it via the Logger.
4. The Logger re-raises the error.
5. class A catches the re-raised error but swallows it completely.
6. The test passes if there are no failing expectations as a result,
even though an exception occurred.
Is there some way to report errors to rspec via a sideband messaging
channel, such that we can implement a logger that would simply report
errors directly to rspec and bypass the need for an exception to bubble all
the way up to the top of the stack in order for a test to fail?
The only possible solution I know of so far would be to set a global
expectation for the logging class not to receive an error message, and
override that behavior in individual tests; however, this feels like the
wrong solution and I'm not sure it would actually work as desired to modify
the expectation behavior from `expect().not_to receive` (in a global before
hook) to `allow().to receive`, or `expect().to receive`. Or we would have
to override the default expectation via metadata in individual tests which
again is not an ideal solution.
Here is a rudimentary example of the problem:
class RSpecLogger
def error(e)
# somehow report sideband error to rspec
end
end
class SomeOtherReporter
def error(e)
puts e.message
end
end
class MyErrorLogger
LOGGERS = [
SomeOtherReporter.new,
RSpecLogger.new
]
def self.error(exception)
LOGGERS.each { |r| r.error(exception) }
raise exception if should_reraise? # This could be replaced entirely
via sideband reporting
end
def self.should_reraise?
true # if in test environment
end
end
describe MyErrorLogger do
it 'should fail even if reraise is caught elsewhere' do
begin
begin
# Something raises an exception
raise StandardError.new("Foo message")
rescue => e
# We rescue and log it
described_class.error(e)
end
rescue => e
# the reraised error is swallowed somewhere further up the stack, so
the test passes if no assertions fail.
nil
end
end
end
Thanks,
Tim
--
You received this message because you are subscribed to the Google Groups
"rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/rspec/d0fcb0da-8578-49f0-be1e-9a9094276fe0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.