为什么要创建模拟对象?

cor*_*ore 25 testing unit-testing mocking

在最近的一次采访中,有人问我为什么要创建模拟对象.我的回答是这样的:"拿一个数据库 - 如果你正在编写测试代码,你可能不希望那个测试实时连接到将要执行实际操作的生产数据库."

从回答来看,我的回答显然不是面试官所寻求的.什么是更好的答案?

Sam*_*ijo 30

我总结如下:

  • 隔离 - 您可以独立测试方法,只测试方法.你的测试成为一个真正的单元测试(最重要的恕我直言)
  • 减少测试开发时间 - 使用模拟然后创建一个完整的类通常更快,只是为了帮助您测试
  • 它允许您测试即使您没有实现所有依赖项 - 您甚至不需要创建例如您的存储库类,并且您将能够测试将使用此存储库的类
  • 让您远离外部资源 - 某种意义上讲,您无需访问数据库,调用Web服务,读取文件,发送电子邮件或向信用卡收费等等...

在一次采访中,我建议包括,当开发人员使用依赖注入时,一旦它允许您拥有更多控制权,并且更容易构建测试,那么模拟会更好.

  • 当然,在模拟对象的*坏*方面,事实是当真正的类接口改变时,某人必须四处寻找并更新Mocks,同时所有这些"成功"的测试都是谎言. (14认同)
  • 如果您的某个测试包括验证接口未更改,则可以减轻这种情况.那个测试失败了,你知道你的模拟会出错. (6认同)

Ben*_*ett 5

在单元测试时,每个测试都旨在测试单个对象.但是,系统中的大多数对象都将具有与之交互的其他对象.模拟对象是这些其他对象的虚拟实现,用于隔离被测对象.

这样做的好处是,任何失败的单元测试通常会将问题与测试对象隔离开来.在某些情况下,问题将出在模拟对象上,但这些问题应该更容易识别和修复.

为模拟对象编写一些简单的单元测试可能是一个想法.

它们通常用于创建模拟数据访问层,以便单元测试可以与数据存储隔离运行.

其他用途可能是在MVC模式中测试控制器对象时模拟用户界面.这允许更好地自动测试UI组件,这可以在某种程度上模拟用户交互.

一个例子:

public interface IPersonDAO
{
    Person FindById(int id);
    int Count();
}

public class MockPersonDAO : IPersonDAO
{
    // public so the set of people can be loaded by the unit test
    public Dictionary<int, Person> _DataStore;

    public MockPersonDAO()
    {
        _DataStore = new Dictionary<int, Person>();
    }

    public Person FindById(int id)
    {
        return _DataStore[id];
    }

    public int Count()
    {
        return _DataStore.Count;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ext*_*kun 5

只是为了在这里添加精细答案,模拟对象用于自上而下或自下而上的结构编程(也是OOP).它们用于向上层模块(GUI,逻辑处理)提供数据或充当模拟输出.

考虑自上而下的方法:首先开发GUI,但GUI应该有数据.因此,您创建一个只返回std :: vector <>数据的模拟数据库.您已经定义了关系的"合同".谁在乎数据库对象内部发生了什么 - 只要我的GUI列表得到一个std :: vector <>我很高兴.这可以提供模拟用户登录信息,无论您需要什么来使GUI工作.

考虑自下而上的方法.您编写了一个读取分隔文本文件的解析器.你怎么知道它是否有效?您为这些对象编写了一个模拟"数据接收器",并将数据路由到那里以验证(尽管通常)数据是否正确读取.上一级的模块可能需要2个数据源,但您只写了一个.

在定义模拟对象时,您还定义了关系的契约.这通常用于测试驱动编程.您编写测试用例,使用模拟对象使其工作,并且通常情况下,模拟对象的接口成为最终接口(这就是为什么在某些时候您可能希望将模拟对象的接口分离为纯抽象类) .

希望这可以帮助