Home | Libraries | People | FAQ | More |
When we're testing a system, we want to test the failure cases as well as the success cases. This means forcing the system under test down an error path by orchestrating bad inputs or synthesizing errors from code collaborating with the system under test.
Let's add the requirement that hello_world
should throw the a std::runtime_error
exception if the
supplied stream has the badbit
set on the stream.
We can add a test case for this:
BOOST_AUTO_TEST_CASE(hello_world_stream_with_badbit_throws_runtime_error) { std::ostringstream dest; dest.clear(std::ios_base::badbit); hello_world(dest); BOOST_FAIL("std::runtime_error not thrown"); }
Note | |
---|---|
The |
Instead of an assertion macro like BOOST_REQUIRE_EQUAL
,
we're using the BOOST_FAIL
macro that guarantees test failure. If the call to hello_world
doesn't throw a runtime_error
exception and continues
to the next line of code, then this test case fails:
Running 2 test cases... src/tutorials/testing_with_exceptions/1/test/test_hello.cpp(24): fatal error in "hello_world_stream_with_badbit_throws_runtime_error": std::runtime_error not thrown *** 1 failure detected in test suite "Master Test Suite" EXIT STATUS: 201
Now let's enhance the implementation to support this requirement:
void hello_world(std::ostream& stream) { if (stream.bad()) { throw std::runtime_error("bad stream"); } stream << "Hello, world!\n"; }
We run our test and get something similar to the following:
Running 2 test cases... unknown location(0): fatal error in "hello_world_stream_with_badbit_throws_runtime_error": std::runtime_error: bad stream src/tutorials/testing_with_exceptions/2/test/test_hello.cpp(13): last checkpoint *** 1 failure detected in test suite "Master Test Suite" EXIT STATUS: 201
Now the test is still failing, so what did we do wrong? The exception from
our system under test unwound the call stack back into the unit test framework,
which caught the exception and reported this as a failure. We need to tell
the test framework that an exception is expected in this situation. We can
use BOOST_REQUIRE_THROW
to tell the test framework that an expression is expected to throw a particular
type of exception. If the exception isn't thrown or an exception of a different
type is thrown, then the test fails. Our test now looks like this:
BOOST_AUTO_TEST_CASE(hello_world_stream_with_badbit_throws_runtime_error) { std::ostringstream dest; dest.clear(std::ios_base::badbit); BOOST_REQUIRE_THROW(hello_world(dest), std::runtime_error); }
Now our test case is passing:
Running 2 test cases... *** No errors detected EXIT STATUS: 0
test_hello.cpp
, which is including the entire
unit test framework source. This leads to a lengthy compile every time
we have to change the tests.