Assertions in subroutines (custom assertions)

1,686 views
Skip to first unread message

Miral

unread,
Oct 14, 2009, 1:42:49 AM10/14/09
to Google C++ Testing Framework
What's the best way to write a re-usable chunk of code that carries
out some actions and performs some asserts/expects as it goes?

I'm imagining something like this:

void Foo(some parameters here)
{
...
EXPECT_EQ(something, x) << "bad x";
EXPECT_EQ(something, y) << "bad y";
ASSERT_GT(a, b) << "bad a,b";
}

something that creates ASSERT_FOO/EXPECT_FOO from Foo

TEST(x)
{
ASSERT_FOO(some parameters here);
EXPECT_FOO(some other parameters here);
}

Where the final output on failure includes references to both the line
in Foo itself that failed and the calling ASSERT_FOO line.

While it's possible to create something that does something similar
with *_PRED_FORMAT*, it doesn't look like I can use ASSERT_* or
EXPECT_* in the predicate routine, which is less convenient and makes
the predicate routine less readable.

I also had a look at using matchers from gmock (and ASSERT_THAT), but
those don't really seem suitable either since there isn't a value to
match against.

Vlad Losev

unread,
Oct 14, 2009, 4:37:42 PM10/14/09
to Miral, Google C++ Testing Framework
Hi Miral,

On Tue, Oct 13, 2009 at 10:42 PM, Miral <go...@mirality.co.nz> wrote:

What's the best way to write a re-usable chunk of code that carries
out some actions and performs some asserts/expects as it goes?

I'm imagining something like this:

void Foo(some parameters here)
{
 ...
 EXPECT_EQ(something, x) << "bad x";
 EXPECT_EQ(something, y) << "bad y";
 ASSERT_GT(a, b) << "bad a,b";
}

something that creates ASSERT_FOO/EXPECT_FOO from Foo

TEST(x)
{
 ASSERT_FOO(some parameters here);
 EXPECT_FOO(some other parameters here);
}

Where the final output on failure includes references to both the line
in Foo itself that failed and the calling ASSERT_FOO line.

We don't have exactly what you are looking but please look at the "Using Assertions in Sub-routines section in GoogleTestAdvancedGuide wiki page.
 
While it's possible to create something that does something similar
with *_PRED_FORMAT*, it doesn't look like I can use ASSERT_* or
EXPECT_* in the predicate routine, which is less convenient and makes
the predicate routine less readable.

I also had a look at using matchers from gmock (and ASSERT_THAT), but
those don't really seem suitable either since there isn't a value to
match against.

You can pass arguments into a struct or tuple and pass that to a matcher:

using ::std::tr1::get;
MATCHER(Foo, "Foo holds") {
  return get<0>(arg) ==  something && get<1>(arg) == something && get<2>(arg) > get<3>(arg);
}

EXPECT_THAT(tuple(x, y, a, b), Foo);

HTH,
Vlad

Miral

unread,
Oct 14, 2009, 8:51:37 PM10/14/09
to Google C++ Testing Framework
On Oct 15, 9:37 am, Vlad Losev <vl...@google.com> wrote:
> We don't have exactly what you are looking but please look at the "Using
> Assertions in Sub-routines<http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Usin...>section
> in GoogleTestAdvancedGuide wiki page.

I have. But use of SCOPED_TRACE with subroutines leads to fugly code
(because to be useful it has to be applied at the callsite, which
requires arbitrary blocks around each call), and it doesn't support
multiple arguments. (See http://code.google.com/p/googletest/issues/detail?id=207.)

And I don't need to use ASSERT_NO_FATAL_FAILURE here because I'm only
using expectations in the subroutine, not assertions.

> You can pass arguments into a struct or tuple and pass that to a matcher:
>
> using ::std::tr1::get;
> MATCHER(Foo, "Foo holds") {
>   return get<0>(arg) ==  something && get<1>(arg) == something &&
> get<2>(arg) > get<3>(arg);
>
> }
>
> EXPECT_THAT(tuple(x, y, a, b), Foo);

Interesting, but that doesn't help in this case since it still gives
no information about what failed *within* the Foo method. What I'm
after is something where any failure within Foo reports the line
number that called Foo, and all the line number(s) within Foo that
failed (since they're only expectations, not assertions).

I've built something using EXPECT_PRED_FORMAT* for now, which is
working ok. (Basically it creates a testing::Message object at the
start and manually streams expectation failures to it as they happen,
and returns it all at the end.) This isn't bad, but it's not
composable.

Miral

unread,
Oct 14, 2009, 9:08:13 PM10/14/09
to Google C++ Testing Framework
Mere moments ago, I wrote:
> Interesting, but that doesn't help in this case since it still gives
> no information about what failed *within* the Foo method.  What I'm
> after is something where any failure within Foo reports the line
> number that called Foo, and all the line number(s) within Foo that
> failed (since they're only expectations, not assertions).

How about this for a design idea:

Define a new wrapper, eg:
ASSERT_SUB(Foo(a, b, c));
(Probably not the best name.)

This pushes an object onto a "test scope stack". While this stack is
non-empty, ASSERT_* and EXPECT_* store their results inside this
object instead of whatever they normally do, but otherwise behave
normally (eg. ASSERT_* still causes the containing method to exit).
When control returns to the ASSERT_SUB line, it pops the object off
the top of the stack and checks whether it accumulated any failures.
If so, it prints its own location and all of the accumulated failures
from the subroutine(s). Finally, it acts like
ASSERT_NO_FATAL_FAILURES and exits the current routine if any fatal
failures occurred in the subroutine. (EXPECT_SUB should behave
similarly but without that final step.)

Vlad Losev

unread,
Oct 14, 2009, 9:47:32 PM10/14/09
to Miral, Google C++ Testing Framework


2009/10/14 Miral <go...@mirality.co.nz>


On Oct 15, 9:37 am, Vlad Losev <vl...@google.com> wrote:
> We don't have exactly what you are looking but please look at the "Using
> Assertions in Sub-routines<http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Usin...>section
> in GoogleTestAdvancedGuide wiki page.

I have.  But use of SCOPED_TRACE with subroutines leads to fugly code
(because to be useful it has to be applied at the callsite, which
requires arbitrary blocks around each call), and it doesn't support
multiple arguments.  (See http://code.google.com/p/googletest/issues/detail?id=207.)

You can hide the ugliness by defining a macro to wrap calls to testing functions:

#define SCOPE_TRACED(statement) { SCOPED_TRACE("in " #statement); statement; }

and then use it for concise scoping:

SCOPE_TRACED(foo(x, y, a, b));

Zhanyong Wan (λx.x x)

unread,
Oct 15, 2009, 1:33:32 AM10/15/09
to Miral, Google C++ Testing Framework
Hi Miral,

On Wed, Oct 14, 2009 at 5:51 PM, Miral <go...@mirality.co.nz> wrote:
>
> On Oct 15, 9:37 am, Vlad Losev <vl...@google.com> wrote:
>> We don't have exactly what you are looking but please look at the "Using
>> Assertions in Sub-routines<http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Usin...>section
>> in GoogleTestAdvancedGuide wiki page.
>
> I have.  But use of SCOPED_TRACE with subroutines leads to fugly code
> (because to be useful it has to be applied at the callsite, which
> requires arbitrary blocks around each call), and it doesn't support
> multiple arguments.  (See http://code.google.com/p/googletest/issues/detail?id=207.)

For multiple arguments, write:

SCOPED_TRACE(Message() << "foo = " << foo);

> I've built something using EXPECT_PRED_FORMAT* for now, which is
> working ok.  (Basically it creates a testing::Message object at the
> start and manually streams expectation failures to it as they happen,
> and returns it all at the end.)  This isn't bad, but it's not
> composable.

I like using SCOPED_TRACE better. Perhaps you can try Vlad's
suggestion. Perhaps we should even make Vlad's macro part of gtest.

--
Zhanyong

Vlad Losev

unread,
Oct 17, 2009, 5:17:08 PM10/17/09
to Miral, Google C++ Testing Framework
Hi Miral,
This can actually be implemented by the client using our new event listener API. It allows you to intercept various events happening inside Google Test including test assertion failures. Want to try your hand in that?

Regards,
Vlad
Reply all
Reply to author
Forward
0 new messages