2

I've been using Boost.Test so far but am now looking into using BDD with Catch instead, but I have some trouble figuring out a nice way of handling exceptions. Say I've got a test like this:

SCENARIO("connection handling", "[network]") {
    boost::asio::io_service io;
    GIVEN("a connection that should fail") {
        connection::ptr conn = connection::create(new fake_provider<connection_refused>(io));
        WHEN("trying to connect") {
            conn->connect("localhost", 1);
            THEN("connection was refused") {
                some stuff to verify exception code
                REQUIRE(conn->connected() == false);
            }
        }
    }
}

Now I'm wondering how to handle the fact that connect() will throw an exception in a nice way. I figure I could save and store the exception in a try-catch and verify under THEN, but that doesn't seem very nice. In my Boost.Test testcases I did this:

bool error_is_connection_refused(boost::system::system_error ex) {
    return ex.code() == boost::system::errc::connection_refused;
}
BOOST_AUTO_TEST_CASE(connect)
{
    connection::ptr conn_refuse = connection::create(new fake_provider<connection_refused>(*io_ptr));
    BOOST_REQUIRE_EXCEPTION(conn_refuse->connect("localhost", 1),
                            boost::system::system_error,
                            error_is_connection_refused);
    BOOST_REQUIRE_EQUAL(conn_refuse->connected(), false);
}

But that doesn't seem very BDD. How do people usually handle exception throwing code when using BDD testing?

dutt
  • 7,909
  • 11
  • 52
  • 85
  • To me your Boost Test solution looks fine. What's the problem you're trying to solve relative to that working solution? – John Zwinck Apr 05 '14 at 09:38
  • Nothing big in reality, there are a couple of minor issues I've had to work around to get boost.test to work(for example getting reports in jenkins since boost.test can't output jUnit compatible xml), and it seems boost.test isn't being developed/maintained anymore. And being curious about BDD after hearing a bit about it. I'll update the question a bit. – dutt Apr 05 '14 at 09:47
  • I think Boost.Test is just very mature--it isn't "maintained" because it does not need to be. You can bet that if there are outright bugs in it, they will get fixed, because the Boost people are the official maintainers and they are responsive. Here is a question which has an answer on how to get Jenkins to understand Boost Test output: http://stackoverflow.com/questions/16537914/using-jenkins-with-boost-test-unit-tests – John Zwinck Apr 05 '14 at 09:53
  • Ah, ok. Fair enough :) I've got boost.test to understand the output with coverage and whatnot, so that's already fixed. I'm still curious about what's the BDD way of handling exceptions though. – dutt Apr 05 '14 at 09:55

2 Answers2

6

I'm a bit late to this as I've only just noticed the catch-unit-test tag :-)

Currently you can do:

REQUIRE_THROWS( <expr> );

or to test for a specific exception type:

REQUIRE_THROWS_AS( <type>, <expr> );

If you want to verify the contents of the exception message... there's not currently a way to do that without catching it yourself. That's a feature I've considered adding, though.

philsquared
  • 22,403
  • 12
  • 69
  • 98
  • Can you show this in the OP's example? I.e. I imagine this has to go at the top of the `WHEN("trying to connect")` block, not in the `THEN("connection was refused")` block where the OP put his comment (and where it feels more natural). (Re: asserting on the exception message: I use this facility in phpUnit - I like the way that it effectively describes why the exception was thrown, so helps to understand the test when reading it 6 months later.) – Darren Cook Apr 22 '15 at 11:22
  • 1
    It would look something like this: `REQUIRE_THROWS_AS( boost::system::system_error, conn->connect("localhost", 1) );` – JBRWilkinson Jul 09 '15 at 19:38
0

This is how to verify exception content as suggested by philsquared

#include <stdexcept>
#include "catch.hpp"

void my_func(double v) 
{
    // ...
}

TEST_CASE("wrong params", "[tag]") {
    SECTION("wrong rate") {
        try {
            my_func(1.1);  // must be in range [0..1]
            REQUIRE_FALSE("must raise issue about rate");
        }
        catch(const std::invalid_argument& e) {
            std::string str (e.what());
            std::string str2 ("rate");
            REQUIRE_FALSE(str.find(str2) == std::string::npos);
        }
    }
}
iutinvg
  • 3,479
  • 3
  • 21
  • 18