如何为非静态容器中的值编写值参数化测试?
How to write value-parameterized tests for values in a non-static container?
我正在尝试编写一个值参数化测试,其中测试值仅在测试 classes 被实例化后创建,即测试值存储在非静态变量中。
这意味着我不能做我通常做的事情,容器是静态的:
INSTANTIATE_TEST_CASE_P(SomeCriteria, SomeTest,
ValuesIn(SomeClass::staticContainerWithTestINputs) );
这是我遇到困难时的 MVCE 示例:
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace testing;
// This is not a test class, so I can't modify `myInt` to be static just so
// that I can write tests.
struct CustomClass
{
int myInt = 0;
};
class Fixture : public ::testing::Test {
protected:
CustomClass myCustomCls;
virtual void SetUp() override
{
// This variable needs to be used in the parameterized test.
myCustomCls.myInt = 42;
}
};
class ValueParamTest : public Fixture, public WithParamInterface<int> {
public:
// The container holding the values to be tested.
const std::vector<int> validInputs {
1, 24, myCustomCls.myInt
};
protected:
virtual void SetUp()
{
Fixture::Fixture::SetUp();
mTestInput = GetParam();
}
int mTestInput;
};
TEST_P(ValueParamTest, ValidInputs)
{
EXPECT_TRUE(mTestInput < 100);
}
// COMPILER ERROR HERE
INSTANTIATE_TEST_CASE_P(ValidInputValues, ValueParamTest,
ValuesIn(ValueParamTest::validInputs) );
编译错误:
59: error: invalid use of non-static data member ‘ValueParamTest::validInputs’
ValuesIn(ValueParamTest::validInputs) );
^
没有那个 ValueParamTest
class 的实例,所以我无法访问它的实例数据成员或成员函数。
任何人都可以提示如何在 GTest 中完成此操作?
我认为可能无法使用动态生成的值实例化 TEST_P
,因为 INSTANTIATE_TEST_CASE_P
是一个宏,它实际上为每个参数值定义了新函数。对于你的问题,我想到的唯一 solution/workaround 就是在正常测试的 for 循环中检查 SUT 的所有输入:
using namespace testing;
// This is not a test class, so I can't modify `myInt` to be static just so
// that I can write tests.
struct CustomClass
{
int myInt = 0;
};
class Fixture : public ::testing::Test {
protected:
CustomClass myCustomCls;
virtual void SetUp() override
{
// This variable needs to be used in the parameterized test.
myCustomCls.myInt = 42;
}
};
class ValueParamTest : public Fixture {
public:
// The container holding the values to be tested.
const std::vector<int> validInputs {
1, 24, myCustomCls.myInt, 101, 99, 102
};
protected:
virtual void SetUp()
{
Fixture::Fixture::SetUp();
}
};
TEST_F(ValueParamTest, ValidInputs)
{
std::for_each(validInputs.begin(), validInputs.end(),
[](int v){ EXPECT_TRUE(v < 100) << "invalid input: " << v; });
}
当然,它会被视为一个具有所有缺点(和优点)的测试用例。
如果我错了,请告诉我。动态生成参数化测试用例会很有趣。
貌似Googletest的宏库没有运行你的要求,但是通过
The Fundamental Theorem of Software Engineering,
你可以这样做:-
main.cpp
#include <gtest/gtest.h>
#include <functional>
#include <memory>
using namespace testing;
struct CustomClass
{
int myInt = 0;
};
class Fixture : public ::testing::Test {
protected:
static std::shared_ptr<CustomClass> & getSpecimen() {
static std::shared_ptr<CustomClass> specimen;
if (!specimen) {
specimen.reset(new CustomClass{42});
}
return specimen;
}
void TearDown() override
{
getSpecimen().reset();
}
};
class ValueParamTest :
public Fixture, public WithParamInterface<std::function<int()>> {
public:
static std::vector<std::function<int()>> validInputs;
protected:
void SetUp() override {
mTestInput = GetParam()();
}
void TearDown() override {
Fixture::TearDown();
}
int mTestInput;
};
std::vector<std::function<int()>> ValueParamTest::validInputs{
[]() { return 1; },
[]() { return 24; },
[]() { return ValueParamTest::getSpecimen()->myInt; }
};
TEST_P(ValueParamTest, ValidInputs)
{
std::cout << "mTestInput = " << mTestInput << std::endl;
EXPECT_TRUE(mTestInput < 100);
}
INSTANTIATE_TEST_CASE_P(ValidInputValues, ValueParamTest,
ValuesIn(ValueParamTest::validInputs) );
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
构建和运行如下:
g++ -Wall -std=c++14 -o gtestrun main.cpp -lgtest -pthread && ./gtestrun
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from ValidInputValues/ValueParamTest
[ RUN ] ValidInputValues/ValueParamTest.ValidInputs/0
mTestInput = 1
[ OK ] ValidInputValues/ValueParamTest.ValidInputs/0 (0 ms)
[ RUN ] ValidInputValues/ValueParamTest.ValidInputs/1
mTestInput = 24
[ OK ] ValidInputValues/ValueParamTest.ValidInputs/1 (1 ms)
[ RUN ] ValidInputValues/ValueParamTest.ValidInputs/2
mTestInput = 42
[ OK ] ValidInputValues/ValueParamTest.ValidInputs/2 (0 ms)
[----------] 3 tests from ValidInputValues/ValueParamTest (1 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (1 ms total)
[ PASSED ] 3 tests.
我正在尝试编写一个值参数化测试,其中测试值仅在测试 classes 被实例化后创建,即测试值存储在非静态变量中。 这意味着我不能做我通常做的事情,容器是静态的:
INSTANTIATE_TEST_CASE_P(SomeCriteria, SomeTest,
ValuesIn(SomeClass::staticContainerWithTestINputs) );
这是我遇到困难时的 MVCE 示例:
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace testing;
// This is not a test class, so I can't modify `myInt` to be static just so
// that I can write tests.
struct CustomClass
{
int myInt = 0;
};
class Fixture : public ::testing::Test {
protected:
CustomClass myCustomCls;
virtual void SetUp() override
{
// This variable needs to be used in the parameterized test.
myCustomCls.myInt = 42;
}
};
class ValueParamTest : public Fixture, public WithParamInterface<int> {
public:
// The container holding the values to be tested.
const std::vector<int> validInputs {
1, 24, myCustomCls.myInt
};
protected:
virtual void SetUp()
{
Fixture::Fixture::SetUp();
mTestInput = GetParam();
}
int mTestInput;
};
TEST_P(ValueParamTest, ValidInputs)
{
EXPECT_TRUE(mTestInput < 100);
}
// COMPILER ERROR HERE
INSTANTIATE_TEST_CASE_P(ValidInputValues, ValueParamTest,
ValuesIn(ValueParamTest::validInputs) );
编译错误:
59: error: invalid use of non-static data member ‘ValueParamTest::validInputs’
ValuesIn(ValueParamTest::validInputs) );
^
没有那个 ValueParamTest
class 的实例,所以我无法访问它的实例数据成员或成员函数。
任何人都可以提示如何在 GTest 中完成此操作?
我认为可能无法使用动态生成的值实例化 TEST_P
,因为 INSTANTIATE_TEST_CASE_P
是一个宏,它实际上为每个参数值定义了新函数。对于你的问题,我想到的唯一 solution/workaround 就是在正常测试的 for 循环中检查 SUT 的所有输入:
using namespace testing;
// This is not a test class, so I can't modify `myInt` to be static just so
// that I can write tests.
struct CustomClass
{
int myInt = 0;
};
class Fixture : public ::testing::Test {
protected:
CustomClass myCustomCls;
virtual void SetUp() override
{
// This variable needs to be used in the parameterized test.
myCustomCls.myInt = 42;
}
};
class ValueParamTest : public Fixture {
public:
// The container holding the values to be tested.
const std::vector<int> validInputs {
1, 24, myCustomCls.myInt, 101, 99, 102
};
protected:
virtual void SetUp()
{
Fixture::Fixture::SetUp();
}
};
TEST_F(ValueParamTest, ValidInputs)
{
std::for_each(validInputs.begin(), validInputs.end(),
[](int v){ EXPECT_TRUE(v < 100) << "invalid input: " << v; });
}
当然,它会被视为一个具有所有缺点(和优点)的测试用例。
如果我错了,请告诉我。动态生成参数化测试用例会很有趣。
貌似Googletest的宏库没有运行你的要求,但是通过 The Fundamental Theorem of Software Engineering, 你可以这样做:-
main.cpp
#include <gtest/gtest.h>
#include <functional>
#include <memory>
using namespace testing;
struct CustomClass
{
int myInt = 0;
};
class Fixture : public ::testing::Test {
protected:
static std::shared_ptr<CustomClass> & getSpecimen() {
static std::shared_ptr<CustomClass> specimen;
if (!specimen) {
specimen.reset(new CustomClass{42});
}
return specimen;
}
void TearDown() override
{
getSpecimen().reset();
}
};
class ValueParamTest :
public Fixture, public WithParamInterface<std::function<int()>> {
public:
static std::vector<std::function<int()>> validInputs;
protected:
void SetUp() override {
mTestInput = GetParam()();
}
void TearDown() override {
Fixture::TearDown();
}
int mTestInput;
};
std::vector<std::function<int()>> ValueParamTest::validInputs{
[]() { return 1; },
[]() { return 24; },
[]() { return ValueParamTest::getSpecimen()->myInt; }
};
TEST_P(ValueParamTest, ValidInputs)
{
std::cout << "mTestInput = " << mTestInput << std::endl;
EXPECT_TRUE(mTestInput < 100);
}
INSTANTIATE_TEST_CASE_P(ValidInputValues, ValueParamTest,
ValuesIn(ValueParamTest::validInputs) );
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
构建和运行如下:
g++ -Wall -std=c++14 -o gtestrun main.cpp -lgtest -pthread && ./gtestrun
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from ValidInputValues/ValueParamTest
[ RUN ] ValidInputValues/ValueParamTest.ValidInputs/0
mTestInput = 1
[ OK ] ValidInputValues/ValueParamTest.ValidInputs/0 (0 ms)
[ RUN ] ValidInputValues/ValueParamTest.ValidInputs/1
mTestInput = 24
[ OK ] ValidInputValues/ValueParamTest.ValidInputs/1 (1 ms)
[ RUN ] ValidInputValues/ValueParamTest.ValidInputs/2
mTestInput = 42
[ OK ] ValidInputValues/ValueParamTest.ValidInputs/2 (0 ms)
[----------] 3 tests from ValidInputValues/ValueParamTest (1 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (1 ms total)
[ PASSED ] 3 tests.