Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best way to use with Catch2 - BDD style #148

Open
chenlijun99 opened this issue Jun 20, 2019 · 5 comments
Open

Best way to use with Catch2 - BDD style #148

chenlijun99 opened this issue Jun 20, 2019 · 5 comments

Comments

@chenlijun99
Copy link

chenlijun99 commented Jun 20, 2019

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.

@richjohnson-wwt
Copy link

I have also been struggling with this issue.

@rollbear
Copy link
Owner

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.

@rollbear
Copy link
Owner

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.

@rollbear
Copy link
Owner

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.

@chenlijun99
Copy link
Author

chenlijun99 commented Jun 17, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants