【问题标题】:googlemock how to use mocking in testgooglemock 如何在测试中使用模拟
【发布时间】:2019-10-22 10:07:00
【问题描述】:

我是 google mock 的初学者,我不知道如何使用它和这个概念。

如果我正在尝试从一个类中测试一个方法,该类从不同的类中调用一些其他方法。 我是否需要从我的测试方法正在调用的不同类中模拟所有这些方法。 这是一个例子:


class A {
public:
    A () {}
    int setnum(int num) {//do some stuff return 1 or 0//
    }


private:
    int _num;          
};


class B {

  public:
    B (){}
    int init(A *a, int number){ 
     if(a->setnum(number))
        return 1;
     return 0;
     }
    void setNum(int num){_num=num;}

  private:
    A *_a;
    int _num;            
};



class C {
  public:
    int doSoemthing(A *a, int number){ 

    if (domore(a,number))
         return 1;
    return 0;
    }

    int domore(A *a, int number){
        if(_b.init(a,number))
            return 1;
        return 0;

        ;}

  private: 
    B _b;        
};

我是否需要模拟测试我的测试方法所需的所有 A 类和 B 类方法? 或者我可以只模拟一个 Class ,并测试这个类是否有效。

【问题讨论】:

    标签: c++ unit-testing googletest googlemock


    【解决方案1】:

    为了使用模拟测试 C 类,您需要为依赖项引入一个接口,而不是在 C 类中使用(这里添加了 BIface)。然后你需要使用 BIface 的依赖注入到 C 类(通过添加的 ctor)。有了它,您将能够测试 B 类和 C 类的交互。 IMO A 类不需要在 CTest 中模拟(但很可能需要在 BTest 中进行测试)

    class A {
    public:
      A() {}                // not needed
      int setnum(int num) { // do some stuff return 1 or 0//
      }
    
    private:
      int _num;
    };
    
    class BIface {
    public:
      virtual ~BIface() = default;
    
      virtual int init(A *a, int number) = 0;
    
      virtual void setNum(int num) = 0;
    };
    
    class B : public BIface {
    
    public:
      B() {} // not needed
      int init(A *a, int number) override {
        if (a->setnum(number))
          return 1;
        return 0;
      }
      void setNum(int num) override {
        _num = num;
      }
    
    private:
      A *_a;
      int _num;
    };
    
    class C {
    public:
      C(BIface &b) : _b{b} {}
      int doSoemthing(A *a, int number) {
    
        if (domore(a, number))
          return 1;
        return 0;
      }
    
      int domore(A *a, int number) {
        if (_b.init(a, number))
          return 1;
        return 0;
    
        ;
      }
    
    private:
      BIface &_b;
    };
    
    class BIfaceMock : public BIface {
    public:
      MOCK_METHOD2(init, int(A *, int));
      MOCK_METHOD1(setNum, void(int));
    };
    
    TEST(CTest, givenDoingMoreWhenInitOfBReturnOneThenReturnOne) {
      // can be done in CTest ctor if more tests are needed to avoid code duplciation
      BIfaceMock bMock{};
      A a{};                 // `a` doesn't need to be mocked in CTest. It shall be mocked in BTest as it is dependency of B class, not C class
      C testedObject{bMock}; // dependency injection of BFace to C
    
      const auto SOME_INT_PARAM = 42;
    
      // Eq mather is used to match both &a and SOME_INT_PARAM. This confirms proper parameters were passed to init
      EXPECT_CALL(bMock, init(&a, SOME_INT_PARAM)).WillOnce(Return(1));
    
      ASSERT_EQ(1, testedObject.domore(&a, SOME_INT_PARAM));
    }
    

    【讨论】:

    • 是的,这正是我开始的方式,但我不确定是否需要模拟 A 类,这只是代码的一部分,它有点复杂我有单例类,我需要重构代码才能进行测试。但这就是我一直在寻找的答案。非常感谢
    【解决方案2】:

    我不是 100% 确定,但在您的示例中,您根本不必使用模拟。您可以在这里非常轻松地创建对象。

    当我期望某个方法会被调用并且应该返回特定值时,我会使用模拟 - 我不是在测试这个方法,而是例如 if-statment:

     A a;
     if(a.method())
     { 
          // some logic 
     }
    
    • 为了操纵如果会得到什么,我会使用这样的模拟:EXPECT_CALL(aMock.method()).WillOnce(Return(true)); 但是您可以在更多情况下使用它(例如:您可以避免创建非常大的类并将其替换为模拟对象)。

    【讨论】:

    • 对不起我的错误,我编辑了代码。这只是一个简单的例子,我有一个非常复杂的代码。所以我的问题是我需要在这个例子中模拟 A 和 B
    • 即使在代码更改之后,您也不必在这里使用模拟。我看到一种情况:int doMore(){ D d; if(d.isValid()){return 1;} return 0; } 看起来像这样(isValud() - 返回一些值)-> 如果您想以简单的方式控制测试流程,您可以使用模拟来完成它。使用模拟,您可以进行两个简单的测试,并测试您的方法是否在适当的条件下返回 1 和 0。
    • 但是如果 doMore() 依赖于 A 类中的方法会怎样
    • 你可以在这种情况下做到这一点,但 IMO 使用这种 od 微不足道的类你可以在测试用例中创建真实的对象(而不是 od nocka)
    猜你喜欢
    • 1970-01-01
    • 2022-09-30
    • 1970-01-01
    • 1970-01-01
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    相关资源
    最近更新 更多