【问题标题】:How to make expectations on a mock object created inside the tested object?如何对在测试对象内部创建的模拟对象产生期望?
【发布时间】:2015-03-22 18:40:11
【问题描述】:

我想对一个如下所示的类进行单元测试:

template <typename T>
class MyClass {
...
    void someMethod() {
        T object;
        object.doSomething();
    }
...
};

我想对这个类进行单元测试,所以我为 T 创建了一个模拟类:

struct MockT {
...
    MOCK_METHOD(doSomething, 0, void());
...
};

那我想在一个测试用例中使用它:

BOOST_AUTO_TEST_CASE(testSomeMethod) {
    MyClass<MockT> myClassUnderTest;
    MOCK_EXPECT(???)....;
    myClassUnderTest.someMethod();
}

我如何对这个对象做出期望?我的第一个想法是将所有创建的MockT 实例存储在构造函数的静态容器中,然后从析构函数的容器中删除它们。如果对象的创建方法与使用它的方法不同,这将起作用,如下所示:

myClassUnderTest.createTheObject();
MOCK_EXPECT(MockT::findMyObject().doSomething);
myClassUnderTest.useTheObject();

但是为此我需要修改我的类的接口,我真的不想这样做。还有什么我可以做的吗?

【问题讨论】:

    标签: c++ unit-testing boost-test turtle-mock


    【解决方案1】:

    如果您不想修改接口或引入额外的间接性,可以使用 Typemock Isolator++。

    template <typename T>
    class MyClass
    {
    public:
        void someMethod()
        {
            T object;
            object.doSomething();
        }
    };
    
        class RealType  //RealType is the actual production type, no injection needed
        {
        public:
            void doSomething(){}
        };
    

    由于 T 是在 someMethod 内部(被测方法内部)创建的,我们需要伪造 T 的 ctor。 FAKE_ALL 就是这样做的。 fakeRealType 上设置的行为将适用于在运行时创建的所有 RealType 实例。默认的 FAKE_ALL 行为是递归伪造,这意味着所有伪造的方法都是伪造的,并将返回伪造的对象。您还可以在任何方法上手动设置您想要的任何行为。

    TEST_CLASS(MyTests)
        {
        public:
    
            TEST_METHOD(Faking_Dependency_And_Asserting_It_Was_Called)
            {
                RealType* fakeRealType= FAKE_ALL<RealType>();
                MyClass<RealType> myClassUnderTest;
                myClassUnderTest.someMethod();
    
                ASSERT_WAS_CALLED(fakeRealType->doSomething()); 
            }
    
        };
    

    Typemock fakes 并不严格,所以你需要编写一个适当的断言来确保你的方法确实被调用了。您可以使用 Typemock 提供的 ASSERT_WAS_CALLED 来完成。

    P.S 我用的是 MSTest。

    【讨论】:

      【解决方案2】:

      您可以将 doSomething 成员函数重定向到 static one,例如

      struct MockT
      {
          void doSomething() {
              soSomethingS();
          }
          MOCK_STATIC_FUNCTION( doSomethingS, 0, void(), doSomething )
      };
      

      那么你的测试将是

      BOOST_AUTO_TEST_CASE(testSomeMethod) {
          MyClass<MockT> myClassUnderTest;
          MOCK_EXPECT(MockT::doSomething).once();
          myClassUnderTest.someMethod();
      }
      

      如果需要,您可以测试对象实例的constructiondestruction,但它可能不会为您的测试带来更多。

      【讨论】:

      • 是否可以从模拟构造函数的操作中获取this?因为那时也许我可以在对象被创建的时候对它做出期望。
      • 不是来自构造函数的动作,它与构造函数具有相同的签名,因此此处为 void(),但如果需要,可以重复相同的习语,例如让构造函数仅充当存根转发到 MOCK_STATIC_FUNCTION 传递“this”。
      【解决方案3】:

      我发现最好的方法是为成员使用共享指针。不幸的是,我不得不因为单元测试而使用额外的间接,但至少它工作得很好。

      template <typename T, typename TFactory>
      class MyClass {
      ...
          void someMethod() {
              std::shared_ptr<T> object = factory();
              object->doSomething();
          }
      ...
          TFactory factory;
      };
      

      然后在测试中它看起来像这样:

      BOOST_AUTO_TEST_CASE(testSomeMethod) {
          std::shared_ptr<T> mockT;
          MockTFactory factory(mockT);
          MyClass<MockT, MockTFactory> myClassUnderTest(factory);
          MOCK_EXPECT(mockT->doSomething).once();
          myClassUnderTest.someMethod();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-07
        • 1970-01-01
        • 1970-01-01
        • 2014-11-14
        • 2011-03-08
        • 2014-12-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多