如何注入模拟对象

How to inject mock object?

本文关键字:模拟 对象 注入 何注入      更新时间:2023-10-16

我正在尝试做一个单元测试来测试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;
}

或者写一个set函数:

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

是否有其他方法来注入代替这两个方法?我在单元测试项目中使用gmock。

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

但是对于那些喜欢把简单的事情复杂化的人来说,几乎没有其他的方法。

1)制作你的CLoginActivity类模板:
template <class TaskImpl>
class CLoginActivityTemplate
{
      CLoginActivityTemplate() { m_pTask = new TaskImpl(); }
};
using CLoginActivity = CLoginActivityTemplate<Task>;

在你的测试中,测试这个实例:

using CLoginActivityTestable = CLoginActivityTemplate<TaskMock>;

然而,这并不总是那么容易——因为通常很难访问这个mock来设置开销。但是您可以定义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();
}

您需要模拟这个工厂类以确保模拟工厂创建模拟对象。也许它看起来不太有希望,但这只是下一点的介绍。

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

TaskCreateStub.cpp应该返回task-mock -而不是真正的task。您还需要访问这个返回的mock对象——这样您就可以对它设置期望。

TaskCreateStub.cpp

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

我强烈建议您不要使用这种链接器级别的mock。仅用于测试无法重新设计的遗留代码,这可能是使用mock的唯一方法。