依赖注入解析和单元测试

时间:2021-04-03 15:48:07

I'm trying to learn dependency injection and have come across a problem, when unit testing the application.

我正在尝试学习依赖注入,并在单元测试应用程序时遇到了问题。

I'm writing a console application and the container is created and initialized in Main(), it's available as a get-property in Program.Container, so anywhere in my application I can call Program.Container.Resolve<..>().

我正在编写一个控制台应用程序,并在Main()中创建并初始化容器,它在Program.Container中作为get属性提供,因此在我的应用程序的任何地方我都可以调用Program.Container.Resolve <..>() 。

I have a ServiceValidator class like this:

我有一个这样的ServiceValidator类:

public class ServiceValidator
{
    private readonly IConfiguration _configuration;
    private readonly IService _service;

    public ServiceValidator(IConfiguration configuration, IService service)
    {
        _configuration = configuration;
        _service = service;
    }

In another class I use

在我使用的另一个班级

ServiceValidator serviceValidator = Program.Container.Resolve<ServiceValidator>();
serviceValidator.VerifyVersion();

It's the call to Program.Container.Resolve that causes me problems in the unit test, as it hasn't been setup.

这是对Program.Container.Resolve的调用,导致我在单元测试中出现问题,因为它尚未设置。

Is that a bad practice, to call resolve on the container? I could create the ServiceValidator instance in Main() and pass the object around, but that seems stupid as it would cause lots of parameters for the objects that are just passed around to the next method.

这是一个不好的做法,在容器上调用解决方案?我可以在Main()中创建ServiceValidator实例并传递对象,但这似乎很愚蠢,因为它会导致很多参数传递给下一个方法。

So I guess it's acceptable to call Resolve within a class, but then the container must be configured for the unit test. How should I do that, should I move the container to another place than the Program class? What would you recommend?

所以我想在类中调用Resolve是可以接受的,但是必须为单元测试配置容器。我应该怎么做,我应该将容器移动到Program类以外的其他地方吗?你会推荐什么?

If it matters, I'm using Unity and C#

如果重要,我正在使用Unity和C#

Thanks :-)

5 个解决方案

#1


Is that a bad practice, to call resolve on the container? I could create the ServiceValidator instance in Main() and pass the object around, but that seems stupid as it would cause lots of parameters for the objects that are just passed around to the next method.

这是一个不好的做法,在容器上调用解决方案?我可以在Main()中创建ServiceValidator实例并传递对象,但这似乎很愚蠢,因为它会导致很多参数传递给下一个方法。

When you use dependency injection all the way, then you won't need to pass lots of parameters to objects. The constructor of each object should have as parameters only those dependencies which it itself uses directly - it won't know about the transitive dependencies of its direct dependencies.

当您一直使用依赖注入时,您将不需要将大量参数传递给对象。每个对象的构造函数应仅具有它本身直接使用的那些依赖项作为参数 - 它不会知道其直接依赖项的传递依赖性。

So if you have a class X which requires a ServiceValidator, then class X will have a constructor parameter of type ServiceValidator. Then if some class Y uses class X, then class Y will have a constructor parameter of type X. Notice that Y knows nothing about ServiceValidator, so you don't need to pass the ServiceValidator from one class to another - the only place where it is used is when constructing X, and that is often done by a DI framework or in only one place in a hand-written factory.

因此,如果您有一个需要ServiceValidator的类X,那么类X将具有ServiceValidator类型的构造函数参数。然后,如果某个类Y使用类X,那么类Y将具有类型X的构造函数参数。请注意,Y对ServiceValidator一无所知,因此您不需要将ServiceValidator从一个类传递到另一个类 - 唯一的地方就是它用于构造X时,通常由DI框架完成,或者仅在手写工厂中的一个地方完成。

Some links for more information:

一些链接以获取更多信息:

#2


I usually allow calls to resolve dependencies from the container in places like main although I still try to keep them to a minimum. What I then do is configure the container in an initialization method of a test class. I have it initialized with fake implementations for any test class which needs to call the container.

我通常允许调用来解决像main这样的容器中的依赖关系,尽管我仍然试图将它们保持在最低限度。然后我做的是在测试类的初始化方法中配置容器。我用任何需要调用容器的测试类的假实现初始化它。

Test classes which don't call anything requiring the container be initialized are then able to ignore it and not use the fakes. I usually use mocks in those instances.

不调用任何需要容器的任何东西的测试类然后能够忽略它并且不使用假货。我通常在那些情况下使用模拟。

I also use the Microsoft Service Locator so that the dependency that I am taking is on something from the .NET Framework instead of on a specific container. This allows me to down the road use anything I want even a home brewed container.

我还使用Microsoft服务定位器,以便我所采用的依赖关系来自.NET Framework而不是特定容器。这使我可以在路上使用任何我想要的东西,甚至是自酿的容器。

#3


You could use a static class as an initializer for your container. Something like BootStrapper.cs would fine. You can then reference the class methods both in your code and tests.

您可以使用静态类作为容器的初始化程序。像BootStrapper.cs之类的东西就好了。然后,您可以在代码和测试中引用类方法。

#4


Well what you're technically doing is a service location in your class.

那么你在技术上做的是你班上的服务地点。

I remember reading this article a while back:

我记得有一段时间读过这篇文章:

http://martinfowler.com/articles/injection.html

For my classes I never try to use Resolve in them. I create the objects through the container when I need them. For Unit testing, I either use some mock library and stub classes.

对于我的课程,我从不尝试在其中使用Resolve。我需要时通过容器创建对象。对于单元测试,我要么使用一些模拟库和存根类。

#5


The problem lies in the fact that you are trying to test the Main method. This method is virtually impossible to unit test.

问题在于您正在尝试测试Main方法。这种方法几乎不可能进行单元测试。

I would argue that it is best not to unit test your Main method because:

我认为最好不要对Main方法进行单元测试,因为:

  • The emphasis of modern unit testing is about design
  • 现代单元测试的重点是设计

  • You should minimize the dependency on configuration in unit tests. Configuration can be tested with smoke or integration tests.
  • 您应该最小化单元测试中对配置的依赖性。可以使用冒烟或集成测试来测试配置。

#1


Is that a bad practice, to call resolve on the container? I could create the ServiceValidator instance in Main() and pass the object around, but that seems stupid as it would cause lots of parameters for the objects that are just passed around to the next method.

这是一个不好的做法,在容器上调用解决方案?我可以在Main()中创建ServiceValidator实例并传递对象,但这似乎很愚蠢,因为它会导致很多参数传递给下一个方法。

When you use dependency injection all the way, then you won't need to pass lots of parameters to objects. The constructor of each object should have as parameters only those dependencies which it itself uses directly - it won't know about the transitive dependencies of its direct dependencies.

当您一直使用依赖注入时,您将不需要将大量参数传递给对象。每个对象的构造函数应仅具有它本身直接使用的那些依赖项作为参数 - 它不会知道其直接依赖项的传递依赖性。

So if you have a class X which requires a ServiceValidator, then class X will have a constructor parameter of type ServiceValidator. Then if some class Y uses class X, then class Y will have a constructor parameter of type X. Notice that Y knows nothing about ServiceValidator, so you don't need to pass the ServiceValidator from one class to another - the only place where it is used is when constructing X, and that is often done by a DI framework or in only one place in a hand-written factory.

因此,如果您有一个需要ServiceValidator的类X,那么类X将具有ServiceValidator类型的构造函数参数。然后,如果某个类Y使用类X,那么类Y将具有类型X的构造函数参数。请注意,Y对ServiceValidator一无所知,因此您不需要将ServiceValidator从一个类传递到另一个类 - 唯一的地方就是它用于构造X时,通常由DI框架完成,或者仅在手写工厂中的一个地方完成。

Some links for more information:

一些链接以获取更多信息:

#2


I usually allow calls to resolve dependencies from the container in places like main although I still try to keep them to a minimum. What I then do is configure the container in an initialization method of a test class. I have it initialized with fake implementations for any test class which needs to call the container.

我通常允许调用来解决像main这样的容器中的依赖关系,尽管我仍然试图将它们保持在最低限度。然后我做的是在测试类的初始化方法中配置容器。我用任何需要调用容器的测试类的假实现初始化它。

Test classes which don't call anything requiring the container be initialized are then able to ignore it and not use the fakes. I usually use mocks in those instances.

不调用任何需要容器的任何东西的测试类然后能够忽略它并且不使用假货。我通常在那些情况下使用模拟。

I also use the Microsoft Service Locator so that the dependency that I am taking is on something from the .NET Framework instead of on a specific container. This allows me to down the road use anything I want even a home brewed container.

我还使用Microsoft服务定位器,以便我所采用的依赖关系来自.NET Framework而不是特定容器。这使我可以在路上使用任何我想要的东西,甚至是自酿的容器。

#3


You could use a static class as an initializer for your container. Something like BootStrapper.cs would fine. You can then reference the class methods both in your code and tests.

您可以使用静态类作为容器的初始化程序。像BootStrapper.cs之类的东西就好了。然后,您可以在代码和测试中引用类方法。

#4


Well what you're technically doing is a service location in your class.

那么你在技术上做的是你班上的服务地点。

I remember reading this article a while back:

我记得有一段时间读过这篇文章:

http://martinfowler.com/articles/injection.html

For my classes I never try to use Resolve in them. I create the objects through the container when I need them. For Unit testing, I either use some mock library and stub classes.

对于我的课程,我从不尝试在其中使用Resolve。我需要时通过容器创建对象。对于单元测试,我要么使用一些模拟库和存根类。

#5


The problem lies in the fact that you are trying to test the Main method. This method is virtually impossible to unit test.

问题在于您正在尝试测试Main方法。这种方法几乎不可能进行单元测试。

I would argue that it is best not to unit test your Main method because:

我认为最好不要对Main方法进行单元测试,因为:

  • The emphasis of modern unit testing is about design
  • 现代单元测试的重点是设计

  • You should minimize the dependency on configuration in unit tests. Configuration can be tested with smoke or integration tests.
  • 您应该最小化单元测试中对配置的依赖性。可以使用冒烟或集成测试来测试配置。