Google Mock for NonVirtual and Private Functions

时间:2018-01-28 07:24:13

标签: c++ unit-testing c++11 c++14 googlemock

我正在尝试为私有/非虚拟/静态函数编写模拟,并且遇到了相同的方法。

这是它的样子......

让我们假设我有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}}进行演示。我的实际用例完全不同。感谢

2 个答案:

答案 0 :(得分:2)

我们在一些测试项目中使用你的类型使用模拟来检查我们使用依赖注入传递的更大类的回调。在我们的例子中,方法被声明为虚拟。

在你的情况下,他们不是。您的模拟实现将隐藏原始实现 - 如果有的话。所以我认为这里没有问题。

答案 1 :(得分:2)

错误的是你没有测试真正的代码,因为:

  • 评论A级
  • 生成一个名称相同的Mock类

这些操作会改变测试中的代码。

可能出错的一个例子:

  1. 在模拟中更改返回类型:long nonVirtual - 之前为int
  2. 测试,即nonVirtual() == 0xFF'FFFF'FFFF(大于INTMAX)正在执行某些操作
  3. 忘记改变真实A - 所以真正的UsingA有分支经过测试但在实际代码中永远无法访问
  4. 示例代码:

    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
        ...
    };
    

    最好的解决方案是(按顺序):

    1. 要孤立地进行测试,你必须隔离类 - 所以要使用依赖注入(虚函数等,基接口等等) - 这有时称为London School of TDD
    2. 测试两个类AUsingA没有任何存根 - 在一个测试用例中测试它们 - 因此您测试实际代码 - 这称为Detroit Shool of TDD
    3. 通过模板代码分隔,对界面有很好的限制 - 这种方法与您的方法最相似:
    4. 关于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作为模拟没有虚函数的类的解决方法。