2009-09-12 136 views
25

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

通过回答来判断,我的回答显然不是面试官所期待的。什么是更好的答案?

回答

30

我总结是这样的:

  • 隔离 - 你可以在上面所说的只测试方法,独立。您的测试成为真正的单元测试(最重要的恕我直言)
  • 减少测试开发时间 - 它通常更快地使用一个模拟,然后创建一个完整的类只是为了帮助你测试
  • 它可以让你的是即使你没有实现所有的依赖关系也要测试 - 你甚至不需要创建,例如,你的存储库类,你就可以测试一个可以使用这个存储库的类
  • 让你远离外部资源 - 有助于您不需要访问数据库或调用Web服务,或O读文件,或发送电子邮件,或收取信用卡,等等...

在接受记者采访时,我建议包括嘲弄,甚至更好,当开发人员使用依赖注入,一旦它允许你有更多的控制权,并且更容易构建测试。

+14

当然,在Mock Objects的*糟糕的一面,事实是,当真正的类接口发生变化时,有人必须运行查找和更新Mocks,同时所有这些“成功”的测试都是谎言。 – 2009-09-12 04:00:13

+6

如果其中一个测试包括验证界面未更改,则可以减轻此影响。那次测试失败了,你知道你的模拟会出错。 – 2009-12-09 23:39:18

+0

但它并没有改变接口的用户损坏的事实。 – Gutzofter 2010-05-16 01:30:57

3

模拟对象/函数在团队中工作时也很有用。如果你正在研究代码库的一部分,而这部分代码库依赖于其他人负责的代码库的不同部分 - 仍在编写或尚未编写 - 模拟对象/函数对于给你一个预期的输出,这样你就可以继续工作而不用等待对方完成任务。

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; 
    } 
} 
5

只是为了添加到这里高档的答案,模仿对象是在自上而下或自下而上的结构编程(OOP太)使用。他们在那里为上层模块(GUI,逻辑处理)提供数据或充当模拟输出。

考虑自顶向下的方法:首先开发GUI,但GUI应该有数据。所以你创建一个模拟数据库,它只是返回一个std :: vector数据的向量<。你已经定义了这种关系的'契约'。谁在乎数据库对象的内部发生的事情 - 只要我的GUI名单得到一个std ::矢量<>我很高兴。这可以提供模拟用户登录信息,无论您需要如何使GUI工作。

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

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

希望这有助于

2

我会在这里走了不同的方向。成株/假装做上述所有的东西,但也许面试官在想嘲弄为使测试通过或失败的一个假的对象。我基于这个xUnit terminology。这可能会导致关于state testing verses behavior/interaction testing的一些讨论。

他们可能一直在寻找的答案是:模拟对象是不是存根不同。存根模拟了被测方法的依赖关系。存根不应导致测试失败。有模拟做这个还检查怎样及何时被调用。基于潜在行为,Mock会导致测试通过或失败。这有利于在测试过程中减少对数据的依赖,但更贴近实施该方法。

当然,这是炒作,它更可能他们只是想你描述磕碰和DI的好处。

0

采取一种不同的方法(如我想嘲笑已经很好地覆盖以上):
“把一个数据库 - 如果你正在写测试代码,您可能不希望该测试迷上了活到生产数据库,其中将执行实际操作。“

IMO是一种不好的方法来说明这个例子的用法,在测试中有或没有模拟过程中,你永远不会把它”连接到prod数据库“,每个开发者都应该有一个开发者本地数据库来测试。你可能会在测试环境数据库,然后可能是UAT,并最终推动你不是在嘲笑避免使用实时数据库,你是在嘲笑,以便不直接依赖于数据库的类不要求你设置数据库。

最后(我接受我可能会得到这方面的一些评论)IMO开发本地数据库是在单元测试中创下了有效的东西,你应该只被击中它在测试代码直接与数据库,并使用嘲笑交互当你te时刺痛间接访问数据库的代码。

相关问题