C# 使用 Moq 和 Unity 为单元测试模拟存储库对象的正确方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2098937/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Proper way to Mock repository objects for unit tests using Moq and Unity
提问by Rob Packwood
At my job we are using Moq for mocking and Unity for an IOC container. I am fairly new to this and do not have many resources at work to help me out with determining the best practices I should use.
在我的工作中,我们使用 Moq 进行模拟,将 Unity 用于 IOC 容器。我对此相当陌生,并且没有很多资源可以帮助我确定我应该使用的最佳实践。
Right now, I have a group of repository interfaces (Ex: IRepository1, IRepository2... IRepository4) that a particular process needs to use to do its job.
现在,我有一组存储库接口(例如:IRepository1、IRepository2...IRepository4),特定进程需要使用它们来完成其工作。
In the actual code I can determine all of the IRepository objects by using the IOC container and using the RegisterType() method.
在实际代码中,我可以通过使用 IOC 容器和使用 RegisterType() 方法来确定所有 IRepository 对象。
I am trying to figure out the best way to be able to test the method that needs the 4 mentioned repositories.
我试图找出能够测试需要 4 个提到的存储库的方法的最佳方法。
I was thinking I could just register a new instance of the Unity IOC container and call RegisterInstance on the container for each mock object passing in the Mock.Object value for each one. I am trying to make this registration process reusable so I do not have to keep doing the same thing over and over with each unit test unless a unit test requires some specific data to come back from the repository. This is where the problem lies... what is the best practice for setting up expected values on a mocked repository? It seems like if I just call RegisterType on the Unity container that I would lose a reference to the actual Mock object and would not be able to override behavior.
我在想我可以只注册一个新的 Unity IOC 容器实例,并在容器上为每个模拟对象调用 RegisterInstance,为每个模拟对象传递 Mock.Object 值。我试图使这个注册过程可重用,所以我不必在每个单元测试中一遍又一遍地做同样的事情,除非单元测试需要一些特定的数据从存储库中返回。这就是问题所在......在模拟存储库上设置预期值的最佳实践是什么?似乎如果我只是在 Unity 容器上调用 RegisterType,我将失去对实际 Mock 对象的引用并且无法覆盖行为。
采纳答案by Mark Seemann
Unit tests should not use the container at all. Dependency Injection (DI) comes in two phases:
单元测试根本不应该使用容器。依赖注入(DI)分为两个阶段:
- Use DI patterns to inject dependencies into consumers. You don't need a container to do that.
- At the application's Composition Root, use a DI Container (or Poor Man's DI) to wire all components together.
- 使用 DI 模式将依赖项注入消费者。你不需要一个容器来做到这一点。
- 在应用程序的Composition Root 处,使用 DI Container(或Poor Man's DI)将所有组件连接在一起。
How not to use any DI Container at all for unit testing
如何完全不使用任何 DI 容器进行单元测试
As an example, consider a class that uses IRepository1. By using the Constructor Injectionpattern, we can make the dependency an invariant of the class.
例如,考虑一个使用 IRepository1 的类。通过使用构造函数注入模式,我们可以使依赖项成为类的不变量。
public class SomeClass
{
private readonly IRepository1 repository;
public SomeClass(IRepository1 repository)
{
if (repository == null)
{
throw new ArgumentNullException("repository");
}
this.repository = repository;
}
// More members...
}
Notice that the readonly
keyword combined with the Guard Clause guarantees that the repository
field isn't null if the instance was successfully instantiated.
请注意,如果实例成功实例化,readonly
关键字与 Guard 子句结合保证该repository
字段不为空。
You don't need a container to create a new instance of MyClass. You can do that directly from a unit test using Moq or another Test Double:
您不需要容器来创建 MyClass 的新实例。您可以使用 Moq 或其他 Test Double 直接从单元测试中执行此操作:
[TestMethod]
public void Test6()
{
var repStub = new Mock<IRepository1>();
var sut = new SomeClass(repStub.Object);
// The rest of the test...
}
See herefor more information...
请参阅此处了解更多信息...
How to use Unity for unit testing
如何使用 Unity 进行单元测试
However, if you absolutely must use Unity in your tests, you can create the container and use the RegisterInstance method:
但是,如果您绝对必须在测试中使用 Unity,则可以创建容器并使用 RegisterInstance 方法:
[TestMethod]
public void Test7()
{
var repMock = new Mock<IRepository1>();
var container = new UnityContainer();
container.RegisterInstance<IRepository1>(repMock.Object);
var sut = container.Resolve<SomeClass>();
// The rest of the test...
}