Reputation: 5951
I am writing up some tests for a C++ program and I would like to check that my program throws certain types of exceptions when given certain inputs. I have seen that this is doable using external libraries such as googletest, but I would like to know how this was implemented.
I would like to separate the test data from the test code as much as possible. In particular, I would like something like this:
void RunTests(InputList inputs) {
for (int i = 0; i < inputs.length; i++) {
if (FunctionIAmTesting(inputs[i].value) has the expected exception behavior) {
// Pass
} else {
// Fail
}
}
}
InputList inputs = InputList({
Input(5), // no exception when 5 is an input
Input<MyExceptionClass>(0), // MyExceptionClass thrown when 0 is an input
Input<MyOtherExceptionClass>(-1) // MyOtherExceptionClass thrown when -1 is an input
});
RunTests(inputs);
Upvotes: 1
Views: 116
Reputation: 661
A several years back I wrote some simple library for "mocking" objects. And my goal was to check everything related to function calls. In the tests I wrote something like that:
MyMockedObject my;
mock::expect(my, "foo").in(10).out(20).returns(30);
mock::expect(my, "bar").throws(logic_error("bar failed"));
int v;
// test that my::baz() invokes my.foo(10, v)
// then my.bar which fails with the exception
my.baz();
Your task seems to be a little bit easier. All that you need is a way how to describe your expectations and some hack in the test runner to verify them at the end of a test (accordingly to the input). Your expectations are exceptions, just construct them somehow and associate with the input. In your example you did a half part of your work.
typedef std::map<Input, Exception> Expectations;
typedef std::pair<Input, Exception> Expectation;
// somewhere before the tests
expectations.insert(make_pair(Input(5)), NoThrowAnything);
expectations.insert(make_pair(Input(0)), MyException("some message"));
expectations.insert(make_pair(Input(-1)), MyOtherException("another message"));
void run_test(const Expectation& expect)
{
try {
// run the real test here based on Input (expect.first)
check_expectation(expect);
} catch (const Exception& ex) {
check_expectation(expect, ex);
}
}
void run_all_tests(const Expectations& expects)
{
for (e : expects) {
try {
run_test(e);
} catch (const ExpectationException ex) {
// failed expectation
}
}
}
void check_expectation(const Expectation& expect)
{
if (expect.second != NoThrowAnything) {
throw ExpectationFailure(expect);
}
}
void check_expectation(const Expectation& expect, const Exception& ex)
{
if (expect.second != ex) {
throw ExpectationMismatch(expect, ex);
}
}
Upvotes: 0
Reputation: 26720
If you know what the type of exception you are looking for then you can target an exception of that type in the catch ()
statement.
try {
// Test code.
// Unexpected success
std::cerr << "Expected a RelevantException to be thrown." << std::endl;
}
catch (RelevantException& e)
{
// Expected exception, continue.
}
catch (...) // Catch all
{
// Unexpected exception
std::cerr << "Unexpected exception encountered, expected "
"RelevantException." << std::endl;
}
Upvotes: 2