Tests are written to prevent errors from happening. The experienced programmer knows that errors are inevitable, and seeks to anticipate them by writing tests that deal specifically with errors.
There are three basic cases to deal with when testing errors:
no error is raised
an external error (an error class not in the code under test) is raised
an internal error (a custom error class in the code under test) is raised
There are two basic decisions to make when writing code that raises an error.
The first is whether to allow an error to be raised or to attempt to recover from it with defensive practices, such as using a rescue
block or fixing inputs that could cause an error to be raised. In general, lower-level, library code should expose errors without trying to recover from them, allowing the consumer of the code to handle error cases on their own. Higher-level application code should strive to recover from errors more aggressively, allowing only truly unrecoverable errors...