如何模拟采用已经定义的lambda函数并覆盖lambda函数的函数

softronic413

我正在使用googletest学习单元测试,不确定如何覆盖某个功能。简而言之,我需要覆盖在母函数M内定义的lambda函数(比方说L)。M调用一个函数C(在另一个文件中定义),该函数将lambda函数L(回调)作为参数。我正在为M编写单元测试,需要调用M,模拟外部函数M,同时还要确保模拟C和正确覆盖L。

简而言之,对象-> M有L个,M个调用C(L)。

谷歌测试中有没有办法做到这一点?

我已经尝试过的总体形状:

    /* source code */
    /* header */
    struct Object
        {/*struct methods*/ 
        //M declaration
        int M();
        };

    /* cpp file */
    int M()
    {
        /* some operations */
        auto L = [](int number){/* operations, returns 0; */};

        int store; //storing the result of C
        store = C(L);
    }

    /* other file */
    int C(int* L(int))
    {
        /* some operations */
        L(1);

        return some_int;
    }

单元测试文件代码:

    #include <gmock/gmock.h>
    #include <gtest.h>

    using ::testing::Return;
    using ::testing::ReturnRef;
    using ::testing::DoAll;
    using ::testing::SetArgReferee;
    using ::testing::SetArgPointee;
    using ::testing::SetArrayArgument;
    using ::testing::_;

   /* mock class*/
   class MockedFunctions
   {
   public:
       /* going to put 5 ints for the sake of the example */
       MOCK_METHOD1(C, int(int));
   };

   class TestObject : public ::testing::Test
   {
   public:
       TestObject(){}
       ~TestObject(){}
   protected:
       Object *objectInstance;
       virtual void SetUp()
       {    objectInstance = new Object;}
       virtual void TearDown()
       {    delete objectInstance;}
   };

   /* test for function */
   TEST_F(TestObject, test_M)
   {
       MockedFunctions test_C;

       EXPECT_CALL(test_C, C(_))
           .Times(1)
           /* don't care about passed number to L */
           .WillOnce(DoALL (SetArgPointee<0>(L(3)), Return(0));
       /* coud put EXPECT_EQ as well */
       objectInstance->M();
   }

这给我在.WillOnce的错误,指出未在此范围内声明L。

请注意,我不关心L的内容,只要覆盖它们即可。到目前为止,我发现的关于该主题的内容建议模拟我的lambda函数L,在这里我不想做,因为我需要覆盖它的代码作为函数M的一部分。

虽然我不是必须的,但在这种情况下,请严格使用GTest样式(因为我的导师不知道如何进行L函数覆盖),并且可以对C使用存根,这将强制使用L(我有这个目前已实现的版本,以便能够与其余代码进行编译),是否仍然可以严格使用googletest样式获得此报道?

谢谢!

softronic413

最终找到解决方案并将其发布以供参考;

我的解决方案是找到一种方法来在容器中存储我感兴趣的回调方法,为此创建辅助方法并从该向量中获取一些输出。

  1. 在包含MOCK_METHODX()声明的类内部,我首先使用typedef来更轻松地标识我的函数。
  2. 然后,在私有块中声明向量和一个void指针,这些指针将用于传递函数。
  3. 辅助方法,以在向量的x位置获取内容(在某些情况下,我会在测试中多次运行模拟函数,因此希望了解每个调用的执行方式),添加到向量中,清除内容(为其他测试重置)等等。
  4. 类型为MockedFunctions的静态指针,用于在提取所需数据后提供一个对象以“恢复”正常的模拟控件行为。

    //inside my header file.
    #include <gmock/gmock.h>
    #include <vector>
    
    class MockedFunctions {
        public:
           //lets say function L has params (int, void*)
           typedef int (myCb) (int arg_from_cb_L, void* objptr_from_cb_L));
           MockedFunctions(){}
           virtual ~MockedFunctions(){}
    
           //giving C more arguments than just the callback L
           MOCK_METHOD2(C, int(int arg1, int(*L)(int, void*));
        private:
           //note due to namespace limitations, I'm including the std prefix
           std::vector<myCb> callback_storage;
           void *cb_obj_copy;
        public:
           /* Multiple public blocks just for readability purposes.
            * Note the internal usage of vector methods.
            */
           void cb_clear(){
               if(callback_storage.empty())
               {
                   callback_storage.clear();
               }
           }
           void cb_add(myCb callback){
               callback_storage.push_back(callback);
           }
           myCb* cb_result_at(int i){
               return callback_storage.at(i);
           }
           //to store one of the params from the passed callback
           void cb_copy_obj(void* obj){
               cb_obj_cb = obj;
           }
           void cb_get_obj_copy(){
               return cb_obj_copy;
           }
       };
    
       class TestMethods : public ::testing::Test
          {
          public:
              static std::unique_ptr<MockedMethods> testMockedMethods;
              TestMethods(){
                  /* as we call other methods from MockedMethods apart from C,
                   * it would trigger warnings. Using NiceMock hides them for
                   * cleaner output.
                   */
                  testMockedMethods.reset(new ::testing::NiceMock<MockedMethods>());
              }
              ~TestMethods() override{
                  testMockedMethods.reset();
              }
              virtual void SetUp() override {}
              virtual void TearDown() override {}
          };
    

现在,在我的cpp文件中,我定义了C并实例化了指针。

              std::unique_ptr<MockedMethods> TestObject::testMockedMethods(new MockedMethods() );
              int C ( int arg1, int(*L)(int arg1_cb, void* arg2_cb)){
                  TestMethods::testMockedMethods->cb_add(L);
                  TestMethods::testMockedMethods->cb_copy_obj(arg2_cb);
                  /* to avoid complications, took L's return type of int and put a 0
                   * directly, we don't care at this point since we will overload the
                   * method afterwards.
                   */
                  return TestMethods::testMockedMethods->C(arg1, 0);
              }

至于实际测试,请放置在您认为合适的位置:

       class TestCaseClass : public TestMethods
       {
           public:
               TestCaseClass(){}
               ~TestCaseClass(){}
           protected:
               //Here, object is the struct name mentioned previously in the question.
               Object *instance;
               // ...

               virtual void SetUp()
               {
                   instance = new Object();
                   // ...
               }
               virtual void TearDown()
               {
                  delete instance;
               }
       };

       TEST_F(TestCaseClass, test_M)
       {
           // testing method M from struct Object

           /* Run tests normally on C, results will be stored. By our definition, a different object
            * will be the one calling C, so pay attention to it.
            */
           EXPECT_CALL(*testMockedMethods, C(_, _))
               .Times(1)
               .WillOnce(Return(1))
           // I noticed at this point M above did not return anything, suppose it returns store
           EXPECT_EQ(instance->M(), 1);

           // Now to get coverage or data from our callback. First, get the method in cb_obj
           MockedMethods::myObj* cb_method = testMockedMethods->cb_result_at(0);
           // Now get one of its arguments, like that void*. Note we're using our get-ers
           void* mock_object = testMockedMethods->cb_get_obj_copy();
           /* And finally, call our method, finally getting coverage. You can pass 
            * various parameters for branching.
            */
           cb_method(0, mock_object);

           // Don't forget we clear the vector of contents in case we re-use it in another test.
           testMockedMethods->cb_clear();
       }

尽管不是最直接的解决方案,但事实证明它是有效的,我尝试列出一些在创建测试时可能遇到的用例。

祝您编码愉快!

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章