Пересмешивающие классы C ++ с внедрением зависимости - PullRequest
2 голосов
/ 20 апреля 2011

Допустим, вы тестируете класс A, и у него есть внедрение зависимостей из B, которое имеет внедрение зависимостей C.
Таким образом, вы высмеиваете B, но тольков конструктор, который он имеет, требуется инъекция C, так что вам нужно также смоделировать C и внедрить смоделированный C в смоделированный B и только затем ввести его в A?
Что еслиу вас есть 5 последовательных зависимостей?

Какие есть альтернативы?

Я использую Google Mock , так что конкретный ответ также поможет.

Ответы [ 3 ]

6 голосов
/ 21 апреля 2011

У Эмиля правильная идея, вы должны зависеть от интерфейсов, а не от конкретных классов. Так что в вашем примере это будет что-то вроде:

#include <iostream>
using namespace std;

class C {
public:
    int x;
};

class B {
public:
    ~B(){};
    virtual void doSomething() = 0;
};

class ConcreteB : public B{
public:
    ConcreteB(C c) : m_c(c) {}
    void doSomething(){
        std::cout << "HelloWorld" << std::endl;
    }
private:
    C m_c;
};
class A{
public:
    A(B *b): m_b(b){}

    void functionToTestWithSideEffect(){
        m_b->doSomething();
    }
private:
    B *m_b;

};

//#include <gmock/gmock.h>

int main() {
    C c;
    c.x = 42;
    ConcreteB b(c);
    A a(&b);
    a.functionToTestWithSideEffect();
    return 0;
}

В своих тестах вы создаете макет B, который не зависит от какого-либо класса C. Затем вы тестируете интерфейс только с B. Таким образом вы нарушаете зависимость A от C. Создание макета B, который не зависит от С довольно прост:

class MockB : public B {
 public:
  MOCK_METHOD0(doSomething, void());
};
4 голосов
/ 20 апреля 2011

Если вы измените дизайн так, чтобы классы зависели от интерфейсов, а не от конкретных классов, вы избавитесь от проблем конструктора. Помимо улучшения тестируемости, это также может улучшить возможность многократного использования и обслуживания за счет большего количества кода (интерфейсов).

0 голосов
/ 29 апреля 2011

В этом случае вы должны вводить по указателю, а не по ссылке, тогда вы можете передать нулевой указатель. Это сработает, если вы действительно объект, а не фальшивый объект, поэтому он не имеет реальной зависимости от введенного объекта.

Для boost::shared_ptr вы можете сделать следующее:

boost::shared_ptr<C> null_c_ptr;
MockB mock_b(null_c_ptr);
...