我正在尝试为私有/非虚拟/静态函数编写模拟,并且遇到了相同的方法。
这是它的样子......
让我们假设我有class UsingA
需要在class A
{
friend class UsingA;
int privateFn() {}
public:
int nonVirtual() {}
};
// The UsingA class
class UsingA {
A &a1;
public:
UsingA(A & _a1) : a1(_a1) {}
int CallFn() {
return a1.nonVirtual();
}
int CallFn2() {
return a1.privateFn();
}
};
内进行模拟和使用。这两个类的定义类似于
class A
我知道Mocks用于生成类的行为,在创建Mocks时,我们需要从原始类派生。
但是,为了模拟我决定不从原始类派生的行为,而是注释class A
并生成具有相同名称的Mock类,即// Original class A is commented / header file removed
class A {
public:
MOCK_METHOD0(nonVirtual, int());
MOCK_METHOD0(privateFn, int());
};
。
这是我的模拟类看起来像
TEST(MyMockTest, NonVirtualTest) {
A mstat;
UsingA ua(mstat);
EXPECT_CALL(mstat, nonVirtual())
.Times(1)
.WillOnce(Return(100));
int retVal = ua.CallFn();
EXPECT_EQ(retVal,100);
}
TEST(MyMockTest, PrivateTest) {
A mstat;
UsingA ua(mstat);
EXPECT_CALL(mstat, privateFn())
.Times(1)
.WillOnce(Return(100));
int retVal = ua.CallFn2();
EXPECT_EQ(retVal,100);
}
我的测试通常是模拟测试
UsingA
一切正常,我可以通过这个模拟测试friend
。
问题是。
这看起来更容易并且达到目的,但在浏览Google模拟示例时我还没有看到这种示例。如果我这样做会有什么问题吗?
老实说,我没有找到任何。
注意:伙计们,我只使用{{1}}进行演示。我的实际用例完全不同。感谢
答案 0 :(得分:2)
我们在一些测试项目中使用你的类型使用模拟来检查我们使用依赖注入传递的更大类的回调。在我们的例子中,方法被声明为虚拟。
在你的情况下,他们不是。您的模拟实现将隐藏原始实现 - 如果有的话。所以我认为这里没有问题。
答案 1 :(得分:2)
错误的是你没有测试真正的代码,因为:
这些操作会改变测试中的代码。
可能出错的一个例子:
long nonVirtual
- 之前为int
nonVirtual() == 0xFF'FFFF'FFFF
(大于INTMAX
)正在执行某些操作A
- 所以真正的UsingA
有分支经过测试但在实际代码中永远无法访问示例代码:
class A {
public:
MOCK_METHOD0(nonVirtual, long()); // change
MOCK_METHOD0(privateFn, int());
};
void UsingA::processA()
{
if (a.nonVirtual() > VERY_BIG_NUMBER)
{
throw runtime_error("oops");
}
}
TEST_F(UsingATest, throwOnVeryBigNumber)
{
EXPECT_CALL(aMock, nonVirtual()).WillOnce(Return(VERY_BIG_NUMBER + 1));
ASSERT_THROW(objectUndertTest.processA());
}
但真正的A
没有改变 - 所以我们在UsingA类中测试不可访问的代码:
class A {
public:
int nonVirtual(); // not changed
...
};
最好的解决方案是(按顺序):
A
和UsingA
没有任何存根 - 在一个测试用例中测试它们 - 因此您测试实际代码 - 这称为Detroit Shool of TDD 关于3 - 您可能会使用以下内容:
template <class T = A>
class UsingA {
T &a1;
public:
UsingA(T & _a1) : a1(_a1) {}
long CallFn() {
using ANonVirtualResult = std::invoke_result_t<&T::nonVirtual>;
static_assert(std::is_same<long, ANonVirtualResult>::value);
return a1.nonVirtual();
}
...
};
在测试中:
class UsingATest : public ::testing::Test
{
protected:
StrictMock<AMock> aMock;
using ClassUnderTest = UsingA<AMock>;
ClassUnderTest objectUnderTest{aMock};
};
TEST_F(UsingATest, useNonVirtual)
{
const auto VALUE = 123456;
EXPECT_CALL(aMock, nonVirtual()).WillOnce(Return(VALUE));
ASSERT_EQ(VALUE, objectUnderTest.CallFn());
}
您可能会注意到,有关A
的某些假设可能会在编译期间作为static_assert
或通过某些SFINAE技术(更复杂)进行测试。
实际上,有examples with template code in googlemock作为模拟没有虚函数的类的解决方法。