Мокайте посередине с помощью C++/Gmock

У меня есть класс A, который создает объект класса B в своем конструкторе. Я хочу протестировать A с фиктивным объектом B.

И нет, я не могу передать объект B в качестве параметра. Есть ли другой способ?

Я видел статью http://www.ibm.com/developerworks/library/j-mocktest/index.html с интересной темой "Mock in the Middle", но это на Java. Возможно ли это на С++?

class B {...};

class A {
  private:
    B* b;

  public:
    A() {
        b = new B();
    }
    ~A() {..}
};

РЕДАКТИРОВАТЬ:

В общем случае объект может быть создан каким-либо другим методом по запросу. Например,

class A {
    ...
    int doSomething() {
        // Create an object of class B
        b = new B();
    }
}; 

person Sailesh    schedule 18.06.2014    source источник
comment
вы можете создать новый конструктор взять b?   -  person Bryan Chen    schedule 19.06.2014
comment
Я бы мог, но это один из примеров, когда объект создается внутри класса. В общем случае объекты можно создавать по требованию, например, в методе. Но это хороший момент, позвольте мне отредактировать вопрос, чтобы сделать его более ясным.   -  person Sailesh    schedule 19.06.2014
comment
звучит так, будто вам нужен заводской объект/функция   -  person Bryan Chen    schedule 19.06.2014
comment
Добавьте конструктор с фабрикой для вашего объекта B (или в любом месте, где вам это нужно) и смоделируйте его, как хотите.   -  person Marco A.    schedule 19.06.2014
comment
Не могли бы вы привести пример для этого? Потому что, если я принимаю фабрику в качестве аргумента в конструкторе, то место, где создается экземпляр A, также должно создавать экземпляр этой фабрики, и это все, чего я пытаюсь избежать.   -  person Sailesh    schedule 19.06.2014
comment
Но я согласен с тем, что оба эти пункта вместе решают и проблему общего случая.   -  person Sailesh    schedule 19.06.2014
comment
Еще одна идея пришла мне в голову: вы можете просто добавить код для вызова дружественной фабрики для этого объекта и изменить объект B, заменив его другим. Это будет минимально инвазивно и не изменит места вызова для A.   -  person Marco A.    schedule 19.06.2014


Ответы (1)


Вы можете использовать заводской шаблон


учитывая этот код

class B {
  public:
    virtual std::string name() { return "B::name"; }
    virtual ~B() {}
};

class A {
  private:
    std::unique_ptr<B> b;

  public:
    A() {}
    void createB() {
        b.reset(new B); // you want to replace `new B` with something else right?
    }
    void print() {
        std::cout << (b ? b->name() : std::string()) << std::endl;
    }
    ~A() {}
};

с заводской функцией

class A {
  private:
    std::unique_ptr<B> b;

  public:
    std::function<std::unique_ptr<B>()> b_maker;

    A() {
        // default maker
        b_maker = []{ return std::unique_ptr<B>(new B); };
    }

    A(std::function<std::unique_ptr<B>()> func) {
        b_maker = func;
    }

    void createB() {
        b = b_maker();
    }

    void print() {
        std::cout << (b ? b->name() : std::string()) << std::endl;
    }
    ~A() {}
};

создать A с B по умолчанию таким же

A();

и теперь вы можете поставить mocked B с

A([]{return std::unique_ptr<B>{new MockedB};});

демонстрация


вы также можете сделать b_maker глобальной переменной, поэтому вам не нужно ее передавать (но я не рекомендую это делать)


вы можете сделать это сложным способом с AbstractBFactory, BFactory, MockBFactory, но это слишком много накладных расходов и похоже на Java...

person Bryan Chen    schedule 18.06.2014
comment
Похоже, мне придется в конечном итоге сделать что-то вроде этого. Спасибо за код. - person Sailesh; 19.06.2014