abw333
abw333

Reputation: 5951

C++: check if a certain exception type was thrown without external libraries

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

Answers (2)

dnk
dnk

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

Randy the Dev
Randy the Dev

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

Related Questions