Expect a value within a given range using Google Test
Asked Answered
T

7

29

I want to specify an expectation that a value is between an upper and lower bound, inclusively.

Google Test provides LT,LE,GT,GE, but no way of testing a range that I can see. You could use EXPECT_NEAR and juggle the operands, but in many cases this isn't as clear as explicitly setting upper and lower bounds.

Usage should resemble:

EXPECT_WITHIN_INCLUSIVE(1, 3, 2); // 2 is in range [1,3]

How would one add this expectation?

Torbernite answered 23/2, 2014 at 18:10 Comment(0)
C
24

Google mock has richer composable matchers than google test alone:

#include "gmock/gmock.h"

using namespace ::testing;

// expect that x is >= 1 and <= 3
EXPECT_THAT(x, AllOf(Ge(1),Le(3)));

Maybe that would work for you.

See the googletest matchers.md document under the "Composite Matchers" section, here

Gabriel, I generally try to avoid introducing macros because they don't compose well and you end up with a proliferation of them. We should only really need ASSERT_THAT and EXPECT_THAT once we're using Matchers.

But that's not to say I don't value a built-in range check. I just wouldn't do it with macros. I would do it with a higher-level matcher. So:

--- UPDATE ---

For notational convenience, a function returning such a composite matcher can be easily written:

template <typename T>
auto IsInRange(T lo, T hi) {
    return AllOf(Ge((lo)), Le((hi))));
}

EXPECT_THAT(value, IsInRange(min, max));
ASSERT_THAT(value, IsInRange(min, max));
Chrysanthemum answered 23/2, 2014 at 18:25 Comment(1)
I really like your answer. I wrapped it with a macro in my answer I just added here to make it even simpler to use like this: EXPECT_RANGE(value, min, max); and ASSERT_RANGE(value, min, max);Improvised
W
24

Using just Google Test (not mock), then the simple, obvious answer is:

EXPECT_TRUE((a >= 1) && (a <= 3)); // a is between 1 and 3 inclusive

I find this more readable than some of the Mock based answers.

--- begin edit --

The simple answer above not providing any useful diagnostics

You can use AssertionResult to define a custom assert that does produce useful a useful error message like this.

#include <gtest/gtest.h>

::testing::AssertionResult IsBetweenInclusive(int val, int a, int b)
{
    if((val >= a) && (val <= b))
        return ::testing::AssertionSuccess();
    else
        return ::testing::AssertionFailure()
               << val << " is outside the range " << a << " to " << b;
}

TEST(testing, TestPass)
{
    auto a = 2;
    EXPECT_TRUE(IsBetweenInclusive(a, 1, 3));
}

TEST(testing, TestFail)
{
    auto a = 5;
    EXPECT_TRUE(IsBetweenInclusive(a, 1, 3));
}
Waylon answered 17/8, 2016 at 18:0 Comment(0)
M
7

I would define these macros:

#define EXPECT_IN_RANGE(VAL, MIN, MAX) \
    EXPECT_GE((VAL), (MIN));           \
    EXPECT_LE((VAL), (MAX))

#define ASSERT_IN_RANGE(VAL, MIN, MAX) \
    ASSERT_GE((VAL), (MIN));           \
    ASSERT_LE((VAL), (MAX))
Maida answered 23/5, 2020 at 6:15 Comment(2)
Simple and efficient... until "<<" operator is used after the macro: it will only apply to the second (*_LE) googletest macro.Limbic
I like your work and mentioned it in my new answer here. The answer I added does work well with the << operator to add additional error messages to print.Improvised
S
6

There is a nice example in google mock cheat sheet:

using namespace testing;
MATCHER_P2(IsBetween, a, b,
           std::string(negation ? "isn't" : "is") + " between " + PrintToString(a)
           + " and " + PrintToString(b))
{
    return a <= arg && arg <= b;
}

Then to use it:

TEST(MyTest, Name) {
    EXPECT_THAT(42, IsBetween(40, 46));
}
Sulphurbottom answered 8/5, 2016 at 17:16 Comment(1)
This is the best answer!Disrepute
T
1

In the end I created a macro to do this that resembles other macros in the Google Test lib.

#define EXPECT_WITHIN_INCLUSIVE(lower, upper, val) \
  do { \
    EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val, lower); \
    EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val, upper); \
  } while (0)
Torbernite answered 24/2, 2014 at 20:13 Comment(2)
Unfortunately this introduces a reliance on ::internal:: names that can change without notice. This isn't supported.Chrysanthemum
Why not base it on the public macros, and avoid the aforementioned issue: EXPECT_GE(val, lower); EXPECT_LE(val, upper);Symmetry
I
1

Using an Existing Boolean Function in Google Test which don't need google mock.The link is quite specific.

Here is the example.

// Returns true iff m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
const int a = 3;
const int b = 4;
const int c = 10;

the assertion EXPECT_PRED2(MutuallyPrime, a, b); will succeed, while the assertion EXPECT_PRED2(MutuallyPrime, b, c); will fail with the message

!MutuallyPrime(b, c) is false, where

b is 4

c is 10
Iinden answered 3/5, 2017 at 9:2 Comment(0)
I
0

Looking at the answers here, I really think the most-beautiful and perfect answer is a blend between @Billy Donahue's answer and @Alexander Voitenko's answer.

So, here's what I recommend. I think this should become part of the official googletest/googlemock code base:

Quick summary

// 1. definitions
#define EXPECT_RANGE(val, min, max) EXPECT_THAT((val), \
    ::testing::AllOf(::testing::Ge((min)), ::testing::Le((max))))
#define ASSERT_RANGE(val, min, max) ASSERT_THAT((val), \
    ::testing::AllOf(::testing::Ge((min)), ::testing::Le((max))))

// 2. usage
EXPECT_RANGE(value, min, max);
ASSERT_RANGE(value, min, max);

Details

Here is a more-complete picture:

#include "gmock/gmock.h"

/// Expect or assert that value `val` is within the range of `min` to `max`, 
/// inclusive. ie: `val` is tested to be >= `min` and <= `max`.
/// See:
/// 1. googletest `matchers.md` document under the "Composite Matchers" section, 
///    here:
///    https://github.com/google/googletest/blob/main/docs/reference/matchers.md#composite-matchers
/// 1. [My answer with this code] https://mcmap.net/q/480645/-expect-a-value-within-a-given-range-using-google-test
#define EXPECT_RANGE(val, min, max) EXPECT_THAT((val), \
    ::testing::AllOf(::testing::Ge((min)), ::testing::Le((max))))
#define ASSERT_RANGE(val, min, max) ASSERT_THAT((val), \
    ::testing::AllOf(::testing::Ge((min)), ::testing::Le((max))))


TEST(Simulation, TrivialEndToEnd)
{
    // ...test setup stuff goes here...

    // Usage example: expect that the `distance_traveled_miles` value is within
    // the range 1151.77 to 1151.97, inclusive.
    EXPECT_RANGE(stats->distance_traveled_miles, 1151.77, 1151.97);
}

The error output if the test fails is really nice too. Here's what is printed in the event of a failure:

src/main_unittest.cpp:194: Failure
Value of: (stats->distance_traveled_miles)
Expected: (is >= 1151.77) and (is <= 1151.97)
  Actual: 1151.6666666667204 (of type double)
[  FAILED  ] Simulation.TrivialEndToEnd (0 ms)

You can also add C++-style prints to the error message if desired, using the << output print operator, like this:

EXPECT_RANGE(stats->distance_traveled_miles, 1151.77, 1151.97) 
    << "Your custom failure message goes here.\n";

The line just above will produce this output in the event of a failure:

src/main_unittest.cpp:194: Failure
Value of: (stats->distance_traveled_miles)
Expected: (is >= 1151.77) and (is <= 1151.97)
  Actual: 1151.6666666667204 (of type double)
Your custom failure message goes here.

[  FAILED  ] Simulation.TrivialEndToEnd (0 ms)

References:

  1. I first saw the EXPECT_THAT(x, AllOf(Ge(1),Le(3))); usage in @Billy Donahue's answer here.
  2. https://github.com/google/googletest/blob/main/docs/reference/matchers.md
    1. Learn about the AllOf(m1, m2, ..., mn) composite matcher here under the section "Composite Matchers"
    2. Learn about the Ge() ("greater than or equal to") and Le() ("less than or equal to") matchers here under the section "Multi-argument Matchers"
Improvised answered 20/3, 2023 at 5:18 Comment(3)
Gabriel, I generally try to avoid introducing macros because they don't compose well and you end up with a proliferation of them. We should only really need ASSERT_THAT and EXPECT_THAT once we're using Matchers. But that's not to say I don't value a built-in range check. I just wouldn't do it with macros. I would do it with a higher-level matcher. So: namespace t = ::testing; template <typename T> auto IsInRange(T lo, T hi) { return t::AllOf(t::Ge((lo)), t::Le((hi)))); } EXPECT_THAT(value, IsInRange(min, max)); ASSERT_THAT(value, IsInRange(min, max));Chrysanthemum
I do not think that would be a good addition to the GTest, because something like EXPECT_LE( val1, val2, val3, val4 ) which testsval1 <= val2 <= val3 <= val4 (in a mathematical sense) is much more versatile.Aachen
@tommsch, then I hope you make that, and see if you can get it merged. But, currently they have none of it.Improvised

© 2022 - 2024 — McMap. All rights reserved.