7

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

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

非常感谢您的想法或建议...

4

3 回答 3

10

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

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

在配置这些模拟方面,这取决于情况和您的测试方式。我的偏好是在每次测试中使用最少的必要配置来配置它们。这是准确传达测试对其依赖项的要求的好方法。在这些情况下,一些重复并没有错。

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

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

最后,如果您发现需要大量配置,则可能值得考虑将测试依赖项的接口更改为不那么“健谈”。通过寻找一个有效的抽象来减少被测类所需的调用次数,您将在测试中配置更少,并且很好地封装了该类所依赖的职责。

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

于 2012-08-15T23:30:51.183 回答
2

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

于 2012-08-15T10:20:01.943 回答
1

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

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

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

所以...

  1. 如果很明显组中的每个测试都将使用相同的模拟行为,请在您的通用设置中提供它

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

  3. 如果他们之间的模拟不同,但只是在某种程度上,你仍然希望避免冗余。一个常见的设置对您没有帮助,但您可能想引入一个这样的实用程序PrepareMock(args...)来涵盖不同的情况。这将使您的实际测试方法免于重复设置,但仍然可以让您在它们之间引入任何程度的差异。

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

于 2012-08-15T23:48:17.207 回答