【问题标题】:Gmock const method not called instead calling the original method未调用 Gmock const 方法,而是调用原始方法
【发布时间】:2020-05-18 03:19:41
【问题描述】:

我有定义为的接口 在 .h 文件中

namespace diagnostic{
class class1interface{
virtual int readpowerstate()const =0;
virtual int readparameters() = 0;
}
class class1 : public class1interface{
int readpowerstate()const;
int readparameters();}};

在.cc文件中我有这个功能

int diagnostic::readparameters(){
if(diagnostic::readpowerstate ==1)
    { //Dothis}
else
    {return 0}}

我必须执行 else 部分,因为默认情况下,当我运行程序时会调用 if。所以我尝试如下使用gmock。

class Mock_class : public diagnostic::class1interface{
public:
Mock_class(){}
MOCK_METHOD0(readparameters,int());
MOCK_CONST_METHOD0(readpowerstate,int());};

和我写的 gmock 测试如下 // 测试读取参数失败

TEST_F(TestBase, readParam_failure){
Mock_class mock_class;
class1 *class_dummmy = new class1();
EXPECT_CALL(mock_class, readpowerstate()).WillOnce(Return(0));
class_dummy->readparameters;
EXPECT_EQ(0, class_dummy->readparameters());}

当我执行这个程序时,我得到了错误 错误:实际函数调用计数不匹配 EXPECT_CALL(mock_class, readpowerstate())... 预期:至少被调用一次 实际:从未调用 - 不满意且活跃

由于我是 gmock 的新手,对此有什么解决方案。

【问题讨论】:

  • 抱歉,这是一个拼写错误。 (它是 class1::readpowerstate == 1)。
  • 模拟测试不是这样工作的。您实际上并没有在代码中使用模拟类。您确实创建了一个模拟对象,但您测试了实际的原始类,而不将模拟对象注入系统。也就是说,您应该将Mock_class 注入系统,而不是EXPECT_EQ(0, class_dummy->readparameters());},并使用这个注入的对象验证其他类。
  • 您能否告诉我的代码格式如何将 Mock_class 注入系统,因为我对 gmock 很陌生。
  • 有没有与class1interface通信的类?
  • 我们在另一个类方法中调用 class1interface 作为 int class2::readdata(){ std::unique_ptr<:class1interface> classint = std::make_unique<:class1>() ; int value = classint -> readparameters();返回值;对于在其他类函数中调用的所有类,我们遵循相同的方法。

标签: c++ googletest googlemock


【解决方案1】:

模块测试都是关于在模拟环境中测试一个孤立的模块(如选定类的实例),特别是该模块如何与其他对象通信,以及它如何响应这些调用的各种结果。为了使这成为可能,人们使用 mocks 代替其他 real 对象。模拟类允许两者:

  • 配置对特定函数的调用数量/顺序和/或这些调用的参数值等方面的预期,并通过注册所有交互来验证它们;
  • 对这些预期调用的结果进行编程,这些调用返回的结果就像真实对象执行了某些操作并响应了被测对象一样。

这使得测试一个对象成为可能,就好像它被真实的组件包围一样,而实际上没有这些组件。例如,可以通过模拟一个表示设备的类并对该模拟进行编程以在管理器调用某些状态函数时返回错误代码,来测试设备管理器如何响应设备故障。也就是说,没有使用真正的设备类,也不需要连接和配置真正的(有故障的!)设备本身。模拟将假装是那个设备,重要的是,这都将在软件级别上。

然而,只有当类本身的设计方式允许我们以某种方式注入模拟对象来代替它们的真实对象时,这才是可能的。最重要的是,系统中的对象需要通过接口和虚拟调用进行通信。也就是说,前面提到的设备管理器不应该与例如DeviceA(一个具体的类名)通信,而是一些DeviceInterface,因此DeviceA和新创建的模拟DeviceMock都可以实现它接口并在该管理器中使用。这样一来,管理器甚至都不知道它正在测试并与一个模拟对象进行通信,而不是与真正的设备包装器通信。

也就是说,目前,虽然您为 class1interface 创建了一个模拟,但实际上并没有使用该模拟。相反,您尝试测试class1。仅当您的目标是测试通过class1 而不是class1 本身与class1 通信的某些other 组件(如类)时,为class1interface 创建一个模拟是有用的。

所以,模拟class1,你可以例如测试class2。但这需要这个类的设计满足我之前提到的条件:通过接口进行通信和注入模拟类的能力。

所以要满足条件,你必须重写代码:

int class2::readdata()
{
    std::unique_ptr<diagnostic::class1interface> classint
         = std::make_unique<diagnostic::class1>();
    int value = classint->readparameters();
    return value;
}

to(这只是一个值得怀疑的有用性示例,您必须根据自己的需要对其进行调整):

int class2::readdata(diagnostic::classinterface* classint)
{
    int value = classint->readparameters();
    return value;
}

因此您可以为class2 编写一个测试,注入class1 的模拟:

TEST_F( TestClass2, readParam_failure )
{
    // Mocks configuration
    Mock_class mock_class;
    EXPECT_CALL( mock_class, readparameters() ).WillOnce(Return(0));

    // System under test configuration
    diagnostic::class2 sut;

    // Test itself
    EXPECT_EQ( 0, sut.readdata(&mock_class) );
}

这样您可以检查对class2::readdata 的调用是否正确转发到class1interface::readparameters,并返回其结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2012-01-09
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多