如何注入模拟对象?

时间:2016-09-07 07:50:47

标签: c++ dependency-injection googlemock gmock

我正在尝试进行单元测试以测试DoLogin方法:

CLoginActivity::CLoginActivity()
{
    m_pTask = new Task();
}

void CLoginActivity::DoLogin()
{
    m_pTask.execute();
}

其中Task是我需要模拟的另一个类。

class MockTask : public Task
{
public:
    MOCK_METHOD0(Execute, void());

};

要注入MockTask task对象,我必须改变我的构造:

CLoginActivity::CLoginActivity(Task& task)
{
    m_pTask = task;
}

或写一组函数:

CLoginActivity::SetTask(Task& task)
{
    m_pTask = task;
}

是否有其他方法可以注入这两种方法?我在单元测试项目中使用gmock。

1 个答案:

答案 0 :(得分:2)

通过构造函数注入是最好的 - 保持这种设计。

但对于那些喜欢使简单事物复杂化的人来说,很少有其他方法。

1)制作你的CLoginActivity类模板:

template <class TaskImpl>
class CLoginActivityTemplate
{
      CLoginActivityTemplate() { m_pTask = new TaskImpl(); }
};

using CLoginActivity = CLoginActivityTemplate<Task>;

在测试中,测试一下这个实例:

using CLoginActivityTestable = CLoginActivityTemplate<TaskMock>;

然而,它并不总是那么容易 - 因为通常很难访问这个模拟器来设置它。但是您可以定义TestMockWrapper类 确保轻松访问模拟任务:

class TestMockWrapper : public TestMock
{
public:
     static TestMock* lastCreated;
     TestMockWrapper()  { lastCreated = this; }
}; 
using CLoginActivityTestable = CLoginActivityTemplate<TaskMockWrapper>;

2)将工厂对象注入构造函数:

CLoginActivity::CLoginActivity(ITaskFactory& taskFactory)
{
    m_pTask = taskFactory.create();
}

您需要模拟此工厂类以确保mock-factory创建模拟对象。也许它看起来没什么好看的 - 但这只是下一点的介绍。

3)在其他文件中实现特殊工厂功能:

  

CLoginActivity.cpp

#include "TaskCreate.hpp"

CLoginActivity::CLoginActivity()
{
    m_pTask = taskCreate();
}
  

TaskCreate.cpp

// your "real" function here
ITask* createTask() { return new Task(); }

有了这样的设计 - 只使用项目中的选定文件为您的CLoginActivity创建UT测试 - 简单来说 - 在您的UT项目中用TaskcreateStub.cpp替换TaskCreate.cpp:

  1. CloginActivity.cpp
  2. TaskMock.cpp(如果存在)
  3. TaskCreateStub.cpp
  4. TaskCreateStub.cpp应该返回task-mock - 不是真正的任务。您还需要访问此返回的模拟对象 - 因此您可以设置它的期望。

      

    TaskCreateStub.cpp

    // your "real" function here
    static TaskMock* taskMockForCreateStub = nullptr;
    ITask* createTask() { return taskMockForCreateStub ; }
    void setTaskMockForCreateTaskStub(TaskMock* taskMock) { taskMockForCreateStub = taskMock; }
    

    我强烈反对您不使用此类链接器级别的模拟。仅用于测试无法重新设计的遗留代码,这可能是使用mock ...

    的唯一方法