Gtest 和 gmock 在下面的代码片段中虽然错误

Gtest and gmock in below code snippet though error

如何实现privateprotected成员函数的gtest或gmock。我是 gtest 和 gmock 的新手。下面是我在尝试时需要执行 gtest 或 gmock 的代码。

constexpr static char _session[]{"S_ID"};

typedef struct {
    int  session;

} Session;

typedef std::function<void(const Session &)> SessionCallback_t;

class Service : public ParentService {
public:
    Service();

    void registerCallback(const SessionCallback_t & callback);

protected:
    virtual void notifyHandler(const Json::Value & data) override;
    virtual void notifyState();
private:
   
    Session mSession;
    SessionCallback_t mCallback;

    void jsonParse(const Json::Value & json_data);
};

我的 Attemp 没有编译

class TestService : public Service {
        public:
        TestService(): Service() {
        }
        bool registerCallback(const SessionCallback_t & cb) {
           // how to achive this?
              
        }


};


class MyTestService : public ::testing::Test {
    
protected:
  virtual void SetUp() {
  }

  virtual void TearDown() {
    
  }
};

 

TEST_F(MyTestService , registerCallbackTest) {
      TestService service;
       EXPECT_TRUE(service.registerCallback(SessionCallback_t));
    
}   

我被下面的界面卡住了

1.registerCallback()

2.notifyHandler()

3.notifyState()

4.jsonParse()

请通过一些光线继续进行。

欢迎来到 Stack Overflow!

首先,让我在 Designing for Testing 上推荐 CppCast 的这一集。该播客指出,如果您发现您的代码难以测试,则意味着它耦合得太紧,因此设计不佳。

它还(正确地,恕我直言)建议您只测试 public 函数。如果您发现自己需要测试私有函数,您可能应该重构代码。

一种方法是将您的代码分成多个 class 部分,其中包含您要测试的 public 函数。然后,您的组合 class 可以直接创建和拥有 class(如果它是没有依赖关系的基本类型或它自己的复杂资源,例如向量或字符串 class,则适用)或可以使用依赖注入将依赖作为构造函数或方法参数传递(适用于数据库、网络连接、文件系统等)。

然后在测试中,你传入一个测试替身,比如一个模拟对象或一个简化的实现,比如一个 in-memory 数据库而不是一个 out-of-process 数据库连接,它的行为类似于对象但是在测试情况下做你需要的。

这是基本建议。在您的特定情况下,您似乎正试图覆盖 TestService 中的 non-virtual 函数。你到底想测试什么?

我不希望 EXPECT_TRUE(service.registerCallback(SessionCallback_t)); 编译,因为 SessionCallback_t 命名了一个类型,而不是一个类型的实例,所以你不能传入它。再一次,你想做什么完成?


评论更新:

模拟需要虚函数(或鸭子类型)和依赖注入。

如果您只是想测试 registerCallback(),我怀疑您根本不需要模拟。相反,您需要查看函数的文档以了解它所说的将执行的操作——有时称为契约。例如,函数的前置条件和后置条件是什么?它可能遇到的错误情况是什么?这些是单元测试应该涵盖的内容。

例如,它是否只保留一个回调(提示:如所写,是的)?在已经注册回调的情况下调用它会发生什么?它是否容忍传入 default-initialized std::function 个对象?

更大的问题是,您如何验证您的测试是否正确。如果您开始在回调中触发通知,那么您就超出了单独测试此功能的范围。相反,您可以在测试中创建一个访问器 class 来 public 化私有内容,以便您进行验证。尽管如此,您仍无法比较 std::function 是否相等,因此您能做的最好的事情就是调用它并检查是否发生了预期的副作用:

class TestService : public Service {
public:
    const SessionCallback_t& getCallback() const { return mCallback; }
};

struct TestCallback
{
    int mCount = 0;
    void operator()( const Session& ) { ++mCount; }
};

然后在你的测试中,你可以编写如下测试:

TEST_F(MyTestService , Test_registerCallback_BadCallback) {
    auto service = TestService{};
    EXPECT_THROW( service.registerCallback( SessionCallback_t{} ), std::out_of_range );
}   

// Register and check that it's our callback
TEST_F(MyTestService , Test_registerCallback_CallbackSaved) {
    auto service  = TestService{};
    auto callback = TestCallback{};

    EXPECT_TRUE( service.registerCallback( callback ) );
    EXPECT_EQ( callback.mCount, 0 );

    auto actualCallback = service.getCallback();
    EXPECT_TRUE( actualCallback );

    actualCallback();
    EXPECT_EQ( callback.mCount, 1 );
}   

TEST_F(MyTestService , Test_registerCallback_CallbackOverwrite) {
    auto service   = TestService{};
    auto callback1 = TestCallback{};
    auto callback2 = TestCallback{};

    EXPECT_TRUE( service.registerCallback( callback1 ) );
    EXPECT_TRUE( service.registerCallback( callback2 ) );
    EXPECT_EQ( callback1.mCount, 0 );
    EXPECT_EQ( callback2.mCount, 0 );

    auto actualCallback = service.getCallback();
    EXPECT_TRUE( actualCallback );

    actualCallback();
    EXPECT_EQ( callback1.mCount, 0 );
    EXPECT_EQ( callback2.mCount, 1 );
}