trompeloeil
trompeloeil copied to clipboard
Best way to use with Catch2 - BDD style
When writing BDD tests with Catch2, we have something like this:
BarMocksCAPI bar_mock;
SCENARIO("foo") {
GIVEN("foo in reset state") {
WHEN("foo is initialized") {
REQUIRE_CALL(bar_mock, bar_init());
foo_init();
THEN("foo should be initialized") {
CHECK(foo_initialized() == 1)
}
THEN("bar should be initialized") {
// how can we check here that the above REQUIRE_CALL has been met?
}
}
}
}
What I would like to do is to give some mock call expectation a meaningful name, i.e. put it into a THEN block.
I know that I can achieve what I want by using NAMED_*
BarMocksCAPI bar_mock;
SCENARIO("foo") {
GIVEN("foo in reset state") {
WHEN("foo is initialized") {
std::unique_ptr<trompeloeil::expectation> bar_should_be_initialized
= NAMED_REQUIRE_CALL(bar_mock, bar_init());
foo_init();
THEN("foo should be initialized") {
CHECK(foo_initialized() == 1)
}
THEN("bar should be initialized") {
bar_should_be_initialized.reset();
}
}
}
}
But I'm wondering whether there could be a tidier way to express this.
I have also been struggling with this issue.
One thing you can do is to check if the expectation is satisfied.
REQUIRE(bar_should_be_initialized->is_satisfied());
I'm not sure if that is any better, though.
Another way is to use REQUIRE_NOTHROW(bar_should_be_initialized.reset());, but that doesn't really bring anything, other than maybe a more visually pleasing source code.
I realize there are a few too many noexcept in the code, which causes some problems. Unfortunately Catch2 is doing something I don't quite understand in the fail case, so I need to investigate that too.
If you use the catch2 adapter you get a reasonable error message. There's also an improved adapter on the develop branch.
#include <catch2/trompeloeil.hpp>
However, catch2 is behaving in a somewhat confusing way here. If you have a bug that bar_init() is not called from foo_init(), then this bug will be caught and reported twice (once for each THEN block). I don't think there is anything that can be done about that, since it is not really wrong, and quite fundamental to how catch2 works and also to how Trompeloeil works. There is no way to express that you only want this expectation checked in one of the THEN blocks.
Thank you for your effort!
Now that I revisit this problem, maybe this could be a cleanier approach
BarMocksCAPI bar_mock;
BazMocksCAPI baz_mock;
SCENARIO("foo") {
GIVEN("foo in reset state") {
WHEN("foo is initialized") {
std::unique_ptr<trompeloeil::expectation> expected_mock_call;
THEN("bar should be initialized") {
expected_mock_call = NAMED_REQUIRE_CALL(bar_mock, bar_init());
}
THEN("baz should be initialized") {
expected_mock_call = NAMED_REQUIRE_CALL(baz_mock, baz_init());
}
foo_init();
THEN("foo should be initialized") {
CHECK(foo_initialized() == 1)
}
expected_mock_call.reset()
}
}
}
And in case in each THEN block multiple mocking expectations need to be set, maybe a vector of unique_ptr could be used.