如何模拟依赖的成员变量
How to mock a member variable of dependency?
我有一个类和成员:
class A
{
B obj;
public:
int f(int i){return obj.g(i);}
}
这里B obj
是一个依赖项,需要从文件中创建运行时。在A类的单元测试中,我希望用g
类型的int(int)
函数来模拟B obj
。
如何编写测试代码,模拟B obj
,然后测试A::f
。
非常感谢。
您需要使用依赖项注入来实现这一点。为此,让类B
从接口继承,并让类A
持有指向该接口的指针:
class IB
{
public:
virtual void g(int i) = 0;
};
class B : public IB
{
public:
void g(int i) {
// this is your real implementation
}
};
此外,要在类A
中启用依赖项注入,请添加适当的构造函数或setter方法:
class A
{
private:
IB *obj;
public:
A() : obj(nullptr) {}
// You don't need both the constructor and setter, one is enough
A(IB *pB) : obj(pB) {}
// void setB(IB *pB) { obj = pB; }
int f(int i) { return obj->g(i); }
};
现在,在您的生产代码中,您创建了一个B
类的对象,并将其传递给A
类对象(假设我们使用构造函数进行注入):
B b;
A a(&b);
在测试阶段,您创建一个模拟类BMock
,并将该类的一个对象传递给类A
对象:
class BMock : public IB
{
public:
MOCK_METHOD1(g, int(int));
};
TEST(ATests, TestCase1)
{
BMock bmock;
A a(&bmock);
// Now you can set expectations on mock object, for example that g will
// return 100 when it receives 50 as argument
EXPECT_CALL(bmock, g(50)).WillOnce(Return(100));
// Now act by calling A::f, which will in turn call g from mock object,
// and assert on function return value
ASSERT_EQ(a.f(50), 100);
}
您无法使用现有的代码来完成此操作。您已经在A
类中对依赖项进行了硬编码。为了使mocking成为可能,您必须使用一些依赖注入模式。一种可能的方法是有一个指向B
类的指针(更智能),在A
构造函数中,您将获得一个指向B
的指针,用它初始化内部的B*
。这样你就可以在测试中放入一个模拟对象。
要做到这一点,在class A
中取一个指针B而不是对象,并在A
中将单元测试类(FixtureA
)设为friend class
。
class A
{
B *obj;
public:
int f(int i){return obj.g(i);}
friend class FixtureA;
};
在FixtureA.h
中,您可以有以下代码
class FixtureA
{
public:
void Testf();
}
在FixtureA.cpp
中
TEST_F(FixtureA , Testf)
{
Testf();
}
void FixtureA::Testf()
{
A objA;
objA.obj = new BMock();
objA.f(2);
}
在BMock class
中,可以模拟g()
函数。
我知道这个问题已经有几年的历史了,但我在为同样的问题寻找更好的解决方案时偶然发现了这个问题。事实上,我确实有两种可能的解决方案——它们都不是优雅或简单的,但它们确实有效,所以我想在这里分享它们,以防其他人仍然有同样的问题。
我假设您在这里使用的是gcc和ld,但当然这些方法也应该与任何其他编译器一起使用(至少第二个)。
其次,我假设您现在有4个文件,A.hpp、A.cpp、B.cpp和B.hpp,它们具有以下内容(当然,如果您将A.hpp和A.cpp结合起来,它也会起作用…):
A.hpp:
#include "B.hpp"
class A
{
B obj;
public:
int f(int i);
};
A.cpp:
#include "A.hpp"
int A::f(int i){
return obj.g(i);
}
B.hpp:
class B
{
public:
int g(int i);
};
B.cpp包括B::g()的实现。
如果您的文件夹结构中B.hpp与a.cpp不在同一文件夹中,或者将a.cpp中的第1行替换为#include<B.hpp>(我认为这取决于编译器/预处理器,但我不确定这一点。Afaik,gcc总是在中搜索。首先,如果你包括"
在这种情况下,您可以创建另一个B.hpp:
#include <gmock/gmock.h>
class B
{
public:
MOCK_METHOD(int, g, (int), ());
};
现在,您必须将命令行更改为类似的命令行
g++A.cpp-I/测试/B-o A.o
新的B.hpp在哪里/测试/B。此外,在构建测试时,必须从管道中删除B.cpp。
请注意,此方法会更改原始cpp文件中包含的内容,从而更改其对象。如果你不够小心,你的测试很有可能与你的真实系统不同!
方法2:如果B.hpp和A.cpp在同一个文件夹中,并且您有一个使用#include的代码,则可以使用该方法;B.hpp"。在这种情况下,编译器很可能会在源文件夹中使用B.hpp,而不是使用带有-I标志的B.hpp。我认为这种方法更优越,因为它不会改变编译单元A.o,但另一方面它还有更多的工作要做
创建两个文件:Bmock.hp:
#include <gmock/gmock.h>
class Bmock{
public:
MOCK_METHOD(int,g,(int),());
};
B.cpp
#include <B.hpp>
#include <Bmock.hpp>
extern Bmock* bmock;
int B::g(int i){
return bmock->g(i);
}
现在,在链接步骤中,必须用新文件替换原始B.o文件。
测试套件内部:
#include <Bmock.hpp>
#include <A.hpp>
#include <gtest/gtest.h>
Bmock* bmock;
class UT_A:public ::testing::Test{
public:
void SetUp() override{
bmock=new bmock();
}
void TearDown() override{
delete bmock;
}
A uut;
};
TEST_F(UT_A,test_g){
EXPECT_CALL(*bmock,g(5)).WillOnce(Return(3));
ASSERT_EQ(uut.f(5),3);
}
使用这种方法,您的编译单元A.o可以保持不变,因此应该像在真实系统中一样工作。
我希望这对某人有用!
问候
- 如何添加依赖于类本身的模板成员变量
- 初始化依赖于子类的继承类的常量类成员
- 类成员函数参数列表是否可以依赖于模板参数?
- 依赖于类成员属性的类实例成员
- 类成员和依赖项注入的对象与引用
- 依赖注入:依赖关系需求字段/成员
- 依赖模板基础的受保护成员
- 单独库中的类成员函数定义和链接依赖项
- 依赖于依赖类型的非静态数据成员的非限定名称
- 如何让成员函数实现依赖于类的模板参数?
- 具有依赖于实现的成员函数类型的多个静态接口
- 初始化依赖于另一个类成员的类成员
- 如何处理相互依赖并具有模板成员的类
- 模板成员中的循环类依赖项
- 如何模拟依赖的成员变量
- 无法定义依赖类型定义的成员
- 如何强制依赖静态对象(包括模板成员)的初始化顺序
- 在多继承编译器的情况下访问成员变量是否依赖?如何正确地做
- 无效的模板依赖成员函数模板推导 - 认为我正在尝试使用 std::set
- 当无法重新排序时,依赖成员初始化