创建单个共享模拟对象或每个单元测试一个

时间:2012-08-15 10:15:39

标签: unit-testing mocking

我目前正在利用Mock对象扩展我的单元测试(在这种特殊情况下为nSubsitute)。但是我想知道创建Mock对象时当前的智慧。例如,我正在使用一个包含各种例程的对象来获取和处理数据 - 这里没什么大不了的,但它会在相当多的测试中使用。

我是否应该创建一个返回Mock对象的共享函数,其中包含针对大多数Testing项目模拟的所有适当方法和行为,并将该对象调用到我的单元测试中?或者我应该将对象模拟到每个单元测试中,只模拟我测试所需的行为(尽管有时候我会不止一次地嘲笑相同的行为)。

感激地收到了想法或建议......

3 个答案:

答案 0 :(得分:9)

我不确定是否有一个商定的“当前智慧”,但这是我的2美分。

首先,正如@codebox指出的那样,为每个单元测试重新创建模拟是一个好主意,因为您希望单元测试彼此独立运行。否则可能导致测试在一起运行时通过但在单独运行时失败(反之亦然)。创建测试所需的模拟通常在测试设置中完成(NUnit中的[SetUp],XUnit中的构造函数),因此每个测试都将获得一个新创建的模拟。

在配置这些模拟方面,它取决于具体情况和测试方式。我的偏好是在每次测试中配置它们,只需要最少量的配置。这是一种很好的方式来准确地传达测试所需的依赖项。在这些情况下,一些重复没有错。

如果许多测试需要相同的配置,我会考虑使用scenario-based test fixture (链接免责声明:无耻的自我推销)。方案可能类似于When_the_service_is_unavailable,并且该方案的设置可以将模拟服务配置为抛出异常或返回错误代码。然后,每个测试都根据该常见配置/场景进行断言(例如,应显示错误消息,应向管理员发送电子邮件等)。

如果您有大量重复的配置位,则另一个选项是使用Test Data Builder。这为您提供了可重用的方法来配置模拟或其他任何其他测试数据的许多不同方面。

最后,如果您发现需要大量配置,则可能值得考虑将测试依赖项的界面更改为“chatty”。通过查找有效的抽象来减少被测试类所需的调用次数,您将在测试中配置较少,并且可以很好地封装该类所依赖的职责。

值得尝试一些不同的方法,看看什么对你有用。任何删除重复都需要保持平衡,同时保持每个测试用例独立,简单,可维护和可靠。如果您发现大量测试因较小的更改而失败,或者您无法确定单个测试需要的配置,或者测试失败取决于它们的运行顺序,那么您将需要改进你的方法。

答案 1 :(得分:1)

我会为每个测试创建新的模拟 - 如果你重新使用它们,你可能会遇到意想不到的行为,其中早期测试的模拟状态会影响后面测试的结果。

答案 2 :(得分:1)

如果不查看具体案例,很难提供一般答案。

我坚持使用与其他地方相同的方法:首先将测试看作独立的存在,然后寻找相似之处并提取出公共部分。

您的目标是遵循 DRY ,以便在需求发生变化时维护您的测试。

因此...

  1. 如果组中的每个测试都要使用相同的模拟行为,那么请在常用设置中提供

  2. 如果它们中的每一个都有明显的不同,例如:模拟的内容构成您正在测试的重要部分,并且测试/模拟关系看起来像1:1,那么它就是&#39合理地让他们接近测试

  3. 如果它们之间的模拟不同,但只是在某种程度上,您仍然希望避免冗余。常见的SetUp不会对您有所帮助,但您可能希望引入一个涵盖不同情况的PrepareMock(args...)等实用程序。这将使您的实际测试方法无需重复设置,但仍然允许您介绍它们之间的任何程度的差异。

  4. 当您向上提取所有相似性(对于SetUp或辅助方法)时,测试看起来很不错,这样测试方法中唯一剩下的就是它们之间的不同之处。