googlemock как использовать насмешки в тесте

Я новичок в 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 и проверить, работает ли этот класс.


person suadss    schedule 22.10.2019    source источник


Ответы (2)


Чтобы протестировать класс C с помощью макетов, вам необходимо ввести интерфейс для зависимости, который будет использоваться в классе C (здесь добавлен BIface). Затем вам нужно использовать внедрение зависимостей BIface в класс C (через добавленный ctor). Имея это, вы сможете протестировать взаимодействие классов B и C. IMO класс не нужно имитировать в 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));
}
person Quarra    schedule 29.10.2019
comment
да, именно так я и начал, но я не был уверен, нужно ли мне издеваться над классом A. Это всего лишь часть кода, это немного сложно. У меня есть одноэлементные классы, и мне нужно реорганизовать код, чтобы иметь возможность тестировать. Но это тот ответ, который я искал. Огромное спасибо - person suadss; 30.10.2019

Я не уверен на 100%, но в вашем примере вам вообще не нужно использовать макеты. Здесь вы можете очень легко создавать свои объекты.

Я бы использовал макеты, когда ожидал, что какой-то метод будет вызван и должен возвращать определенное значение - я не тестирую этот метод, а, например, if-statment:

 A a;
 if(a.method())
 { 
      // some logic 
 }
  • Чтобы манипулировать тем, что, если получится, я бы использовал такие моки: EXPECT_CALL(aMock.method()).WillOnce(Return(true)); Но вы можете использовать его во многих других ситуациях (например, вы можете не создавать действительно большой класс и заменить его фиктивным объектом).
person thebarylowi    schedule 22.10.2019
comment
Извините за ошибку, редактирую код. Это всего лишь простой пример, у меня очень сложный код. Итак, мой вопрос: нужно ли мне издеваться над A и B в этом примере? - person suadss; 22.10.2019
comment
Даже после изменения кода вам не нужно использовать моки здесь. Я вижу одну ситуацию, если: int doMore(){ D d; if(d.isValid()){return 1;} return 0; } выглядит примерно так (isValud() - возвращает какое-то значение) -> если вы хотите легко контролировать поток тестирования, вы можете использовать mocks для его выполнения. Используя макеты, вы можете провести два простых теста и проверить, возвращает ли ваш метод 1 и 0 с правильным условием. - person thebarylowi; 22.10.2019
comment
Но что, если doMore() зависит от метода в классе A? - person suadss; 22.10.2019
comment
Вы можете сделать это в этом случае, но IMO с такими тривиальными классами вы можете создавать реальные объекты (вместо od nocka) в тестовом примере - person thebarylowi; 22.10.2019