在单元测试中使用依赖注入对象是个坏主意吗?
Is it bad idea to use Dependency Injection objects in unit tests?
我不确定我所做的是否真的是 "correct" 使用 DI 进行单元测试的方法。现在我要求我的 ViewModelLocator 实际创建我需要的所有实例,并且只获取我需要测试的实例,这使得测试单个实例变得非常简单,因为假设 Receipt 需要创建一个 Reseller 对象,reseller 需要要创建一个 User 对象,用户需要创建一些其他对象,这会创建一个对象链来创建只是为了测试一个实例。
使用不同的接口将被模拟并解析为您想要创建的对象,但是简单的 Entities/ViewModels 怎么样?
进行涉及 DI 的单元测试的最佳做法是什么?
public class JournalTest
{
private ReceiptViewModel receipt;
private ViewModelLocator locator;
[SetUp]
public void SetUp()
{
locator = new ViewModelLocator();
receipt = SimpleIoc.Default.GetInstance<ReceiptViewModel>();
}
[TearDown]
[Test]
public void CheckAndCreateNewJournal_Should_Always_Create_New_Journal()
{
receipt.Sale.Journal = null;
receipt.Sale.CheckAndCreateNewJournal();
Assert.NotNull(receipt.Sale.Journal);
}
}
首先,您没有在代码中使用依赖注入。您所拥有的称为服务定位器(服务定位器与 IoC/Service 定位器紧密耦合并使其难以测试)。
是的,这很糟糕(服务定位器和依赖注入),因为这意味着:你不是在进行单元测试,而是在进行集成测试。
在您的情况下,ReceiptViewModel
不会单独测试,但您的测试还会测试 ReceiptViewModel
的依赖项(即存储库、注入的服务等)。这称为集成测试。
UnitTest 必须只测试有问题的 class 而 没有依赖性 。您可以通过存根(依赖项的虚拟实现,假设您已将接口用作依赖项)或使用模拟(使用像 Moq 这样的模拟框架)来实现这一点。
这是 easier/better,因为您不必实现整个 class,而只需为您知道的测试用例所需的方法设置模拟。
另外请注意,您必须自己创建实体。根据您的 UnitTest 框架,可能会有数据驱动的测试(通过测试方法上的属性),或者您只是在代码中创建它们,或者如果您在许多 classes 中使用了 models/entities,请创建一个助手方法。
不应将视图模型注入构造函数(至少应避免),因为它会将它们紧密耦合
单元测试应该 运行 快速并且应该是确定性的。这意味着您必须 mock/stub 违反这两条规则的所有内容。
mock/stub 依赖项的最佳方法是注入它们。在生产中,classes 由 DI 框架 assembled,但在单元测试中,您应该 assemble 手动并在需要的地方注入模拟。
还有一个 classic 单元测试方法,您 stub/mock 您 class 的每个依赖项,但它没有用,因为您不会从中获得任何东西。
Martin Fowler 写了一篇很棒的文章:link
您还应该阅读 发展面向对象的软件:由测试指导 。大量有用的知识。
我不确定我所做的是否真的是 "correct" 使用 DI 进行单元测试的方法。现在我要求我的 ViewModelLocator 实际创建我需要的所有实例,并且只获取我需要测试的实例,这使得测试单个实例变得非常简单,因为假设 Receipt 需要创建一个 Reseller 对象,reseller 需要要创建一个 User 对象,用户需要创建一些其他对象,这会创建一个对象链来创建只是为了测试一个实例。
使用不同的接口将被模拟并解析为您想要创建的对象,但是简单的 Entities/ViewModels 怎么样?
进行涉及 DI 的单元测试的最佳做法是什么?
public class JournalTest
{
private ReceiptViewModel receipt;
private ViewModelLocator locator;
[SetUp]
public void SetUp()
{
locator = new ViewModelLocator();
receipt = SimpleIoc.Default.GetInstance<ReceiptViewModel>();
}
[TearDown]
[Test]
public void CheckAndCreateNewJournal_Should_Always_Create_New_Journal()
{
receipt.Sale.Journal = null;
receipt.Sale.CheckAndCreateNewJournal();
Assert.NotNull(receipt.Sale.Journal);
}
}
首先,您没有在代码中使用依赖注入。您所拥有的称为服务定位器(服务定位器与 IoC/Service 定位器紧密耦合并使其难以测试)。
是的,这很糟糕(服务定位器和依赖注入),因为这意味着:你不是在进行单元测试,而是在进行集成测试。
在您的情况下,ReceiptViewModel
不会单独测试,但您的测试还会测试 ReceiptViewModel
的依赖项(即存储库、注入的服务等)。这称为集成测试。
UnitTest 必须只测试有问题的 class 而 没有依赖性 。您可以通过存根(依赖项的虚拟实现,假设您已将接口用作依赖项)或使用模拟(使用像 Moq 这样的模拟框架)来实现这一点。
这是 easier/better,因为您不必实现整个 class,而只需为您知道的测试用例所需的方法设置模拟。
另外请注意,您必须自己创建实体。根据您的 UnitTest 框架,可能会有数据驱动的测试(通过测试方法上的属性),或者您只是在代码中创建它们,或者如果您在许多 classes 中使用了 models/entities,请创建一个助手方法。
不应将视图模型注入构造函数(至少应避免),因为它会将它们紧密耦合
单元测试应该 运行 快速并且应该是确定性的。这意味着您必须 mock/stub 违反这两条规则的所有内容。
mock/stub 依赖项的最佳方法是注入它们。在生产中,classes 由 DI 框架 assembled,但在单元测试中,您应该 assemble 手动并在需要的地方注入模拟。
还有一个 classic 单元测试方法,您 stub/mock 您 class 的每个依赖项,但它没有用,因为您不会从中获得任何东西。 Martin Fowler 写了一篇很棒的文章:link 您还应该阅读 发展面向对象的软件:由测试指导 。大量有用的知识。