[英]Unit testing the dependency injection
I am using Autofac
for IoC 我正在为IoC使用
Autofac
Here is my container initiator class, which the responsibility is to register the dependencies. 这是我的容器启动程序类,其职责是注册依赖项。
public class ContainerInit
{
public static IContainer BuildContainer()
{
var conFac = new ContainerFactory();
var builder = new ContainerBuilder();
builder.Register(conFac).As<IContainerFactory>().SingleInstance();
builder.Register(c=> new MainClass(conFac)).As<IMainClass>().SingleInstance();
builder.Register(c=> new Database(conFac)).As<IDatabase>().SingleInstance();
var logger = LoggUtil.CreateLogger();
builder.Register(logger).As<ILogger>().SingleInstance();
var container = builder.Build();
ContainerFactory.SetContainer(container);
return container;
}
}
Problem with this approach is, I need to pass IContainerFactory
to the constructor of every class I use in my application as follow 这种方法的问题是,我需要将
IContainerFactory
传递给我在应用程序中使用的每个类的构造函数,如下所示
public class MainClass: IMainClass
{
private readonly ILogger _logger;
private readonly IDatabase _db;
public MainClass(IContainerFactory containerFactory)
{
_logger = containerFactory.GetInstance<ILogger>();
_db = containerFactory.GetInstance<IDatabase>(); //example
}
public AddDetails(Data data)
{
//do some business operations
_db.Add(data);
_logger.Information("added");
}
}
So it is difficult to unit test these classes. 因此,很难对这些类进行单元测试。
How can come up with a good solution? 如何提出一个好的解决方案?
A better approach would be to pass the dependencies you need in your class into your constructor: 更好的方法是将类中需要的依赖项传递给构造函数:
public class MainClass : IMainClass
{
private readonly ILogger _logger;
private readonly IDatabase _db;
public MainClass(ILogger logger, IDatabase db)
{
_logger = logger;
_db = db;
}
public void AddDetails(Data data)
{
//do some business operations
_db.Add(data);
_logger.Information("added");
}
}
Then you could use a mocking framework such as Moq to mock your class dependencies and perform verifications on whether the dependencies were called: 然后,您可以使用诸如Moq之类的模拟框架来模拟您的类依赖关系,并验证是否调用了依赖关系:
[TestClass]
public class UnitTest1
{
private Mock<ILogger> _mockLogger = new Mock<ILogger>();
private Mock<IDatabase> _mockDb = new Mock<IDatabase>();
[TestMethod]
public void TestMethod1()
{
// arrange
var mainClass = new MainClass(_mockLogger.Object, _mockDb.Object);
var data = new Data();
// act
mainClass.AddDetails(data);
// assert
_mockDb
.Verify(v => v.Add(data), Times.Once);
}
}
I would not verify your log message though as this could change and make the test brittle. 我不会验证您的日志消息,因为这可能会改变并使测试变脆。 Only verify functionality which is essential to doing what the method is intended for.
仅验证对执行该方法所必需的功能。
Your current Service Locator Anti-Pattern is what makes your code difficult to test in isolation as well as makes the class misleading about what it actually depends on. 当前的服务定位符反模式使您的代码难以单独进行测试,并使类误解其实际依赖内容。
MainClass
should be refactored to follow Explicit Dependencies Principle 应该重构
MainClass
以遵循显式依赖原则
public class MainClass : IMainClass
private readonly ILogger logger;
private readonly IDatabase db;
public MainClass(ILogger logger, IDatabase db) {
this.logger = logger;
this.db = db;
}
public void AddDetails(Data data) {
//do some business operations
db.Add(data);
logger.Information("added");
}
}
The same pattern should also be followed for any other class you have that depends on the container factory, like Database
. 对于依赖于容器工厂的任何其他类,也应遵循相同的模式,例如
Database
。
You would however need to also refactor the container registration accordingly 但是,您还需要相应地重构容器注册
public class ContainerInit {
public static IContainer BuildContainer() {
var builder = new ContainerBuilder();
builder.RegisterType<MainClass>().As<IMainClass>().SingleInstance();
builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
var logger = LoggUtil.CreateLogger();
builder.Register(logger).As<ILogger>().SingleInstance();
var container = builder.Build();
return container;
}
}
Testing MainClass
would required you to mock only the necessary dependencies of the class under test. 测试
MainClass
将要求您仅模拟被测类的必要依赖关系。
[TestClass]
public class MainClassTests {
[TestMethod]
public void Should_AddDetails_To_Database() {
// Arrange
var mockDb = new Mock<IDatabase>();
var data = new Data();
var mainClass = new MainClass(Mock.Of<ILogger>(), mockDb.Object);
// Act
mainClass.AddDetails(data);
// Assert
mockDb.Verify(_ => _.Add(data), Times.Once);
}
}
Here I would like to share solution, which I use in my project 在这里我想分享我在项目中使用的解决方案
To do unit testing of particular function, I use below structure 为了进行特定功能的单元测试,我使用以下结构
[TestClass]
public class TestSomeFunction
{
public IComponentContext ComponentContext { get; set; }
[TestInitialize]
public void Initialize()
{
//Registering all dependencies required for unit testing
this.ComponentContext = builder.Build(); //You have not build your container in your question
}
[TestMethod]
public void Testfunction()
{
//Resolve perticular dependency
var _logger = containerFactory.Resolve<ILogger>();
//Test my function
//use _logger
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.