标签: dependency-injection

单元测试具有文件系统依赖性的代码

我正在编写一个组件,给定一个ZIP文件,需要:

  1. 解压缩文件.
  2. 在解压缩的文件中查找特定的DLL.
  3. 通过反射加载该DLL并在其上调用方法.

我想对这个组件进行单元测试.

我很想编写直接处理文件系统的代码:

void DoIt()
{
   Zip.Unzip(theZipFile, "C:\\foo\\Unzipped");
   System.IO.File myDll = File.Open("C:\\foo\\Unzipped\\SuperSecret.bar");
   myDll.InvokeSomeSpecialMethod();
}
Run Code Online (Sandbox Code Playgroud)

但人们经常说,"不要编写依赖于文件系统,数据库,网络等的单元测试".

如果我以单元测试友好的方式写这个,我想它看起来像这样:

void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
   string path = zipper.Unzip(theZipFile);
   IFakeFile file = fileSystem.Open(path);
   runner.Run(file);
}
Run Code Online (Sandbox Code Playgroud)

好极了!现在它是可测试的; 我可以将测试双打(模拟)提供给DoIt方法.但是以什么代价?我现在必须定义3个新接口才能使这个可测试.究竟,我在测试什么?我正在测试我的DoIt函数是否正确地与其依赖项交互.它不测试zip文件是否正确解压缩等.

我觉得我不再测试功能了.感觉就像我只是在测试课堂互动.

我的问题是:对依赖于文件系统的东西进行单元测试的正确方法是什么?

编辑我正在使用.NET,但这个概念也可以应用Java或本机代码.

dependencies unit-testing dependency-injection

131
推荐指数
6
解决办法
4万
查看次数

ServiceLocator是反模式吗?

最近我读过Mark Seemann关于Service Locator反模式的文章.

作者指出ServiceLocator为反模式的两个主要原因:

  1. API使用问题(我完全可以使用)
    当类使用服务定位器时,很难看到它的依赖关系,因为在大多数情况下,类只有一个PARAMETERLESS构造函数.与ServiceLocator相比,DI方法通过构造函数的参数显式地暴露依赖关系,因此在IntelliSense中很容易看到依赖关系.

  2. 维护问题(让我感到困惑)
    请考虑以下示例

我们有一个使用服务定位器方法的类'MyType':

public class MyType
{
    public void MyMethod()
    {
        var dep1 = Locator.Resolve<IDep1>();
        dep1.DoSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们要为类'MyType'添加另一个依赖项

public class MyType
{
    public void MyMethod()
    {
        var dep1 = Locator.Resolve<IDep1>();
        dep1.DoSomething();

        // new dependency
        var dep2 = Locator.Resolve<IDep2>();
        dep2.DoSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我的误解开始的地方.作者说:

要判断你是否引入了一个重大改变,要变得更加困难.您需要了解使用Service Locator的整个应用程序,并且编译器不会帮助您.

但是等一下,如果我们使用DI方法,我们将在构造函数中引入与另一个参数的依赖关系(在构造函数注入的情况下).问题仍然存在.如果我们忘记设置ServiceLocator,那么我们可能忘记在IoC容器中添加新的映射,并且DI方法将具有相同的运行时问题.

此外,作者还提到了单元测试的难点.但是,我们不会有DI方法的问题吗?我们不需要更新所有实例化该类的测试吗?我们将更新它们以传递一个新的模拟依赖项,以使我们的测试可编译.我没有看到更新和时间花费带来任何好处.

我不是想捍卫Service Locator方法.但这种误解让我觉得我失去了一些非常重要的东西.有人可以消除我的怀疑吗?

更新(摘要):

我的问题"服务定位器是反模式"的答案实际上取决于具体情况.我绝对不会建议你从工具列表中删除它.当您开始处理遗留代码时,它可能会变得非常方便.如果你很幸运能够处于项目的最初阶段,那么DI方法可能是更好的选择,因为它比Service Locator有一些优势.

以下是主要的不同之处,这些差异使我不相信我的新项目使用Service Locator:

  • 最明显和最重要的是:Service Locator隐藏了类依赖项
  • 如果你正在使用一些IoC容器,它可能会在启动时扫描所有构造函数以验证所有依赖项,并立即给你关于缺失映射(或错误配置)的反馈; 如果您将IoC容器用作服务定位器,则无法进行此操作

有关详细信息,请阅读下面给出的优秀答案.

design-patterns dependency-injection anti-patterns service-locator

127
推荐指数
4
解决办法
3万
查看次数

必须依赖注入以牺牲封装为代价吗?

如果我理解正确,依赖注入的典型机制是通过类的构造函数或通过类的公共属性(成员)注入.

这暴露了注入的依赖性并违反了封装的OOP原则.

我在确定这种权衡时是否正确?你是如何处理这个问题的?

另请参阅下面我对自己问题的回答.

oop encapsulation dependency-injection inversion-of-control

123
推荐指数
7
解决办法
9531
查看次数

ContextLoaderListener与否?

标准的Spring Web应用程序(由Roo或"Spring MVC Project"模板创建)使用ContextLoaderListener和创建一个web.xml DispatcherServlet.为什么他们不仅使用DispatcherServlet并使其加载完整的配置?

我知道ContextLoaderListener应该用于加载非Web相关的东西,而DispatcherServlet用于加载与Web相关的东西(Controllers,...).这导致两个上下文:父上下文.

背景:

几年来我一直以这种标准方式做这件事.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Handles Spring requests -->
<servlet>
    <servlet-name>roo</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/webmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
Run Code Online (Sandbox Code Playgroud)

这通常会导致两个上下文及其之间的依赖关系出现问题.在过去,我始终能够找到解决方案,我强烈认为这使得软件结构/架构总是更好.但现在我面临着两种情境事件的问题.

- 然而这让我重新思考这两个上下文模式,我问自己:为什么我要把自己带入这个麻烦,为什么不用一个加载所有弹簧配置文件DispatcherServletContextLoaderListener完全删除.(我仍然会有不同的配置文件,但只有一个上下文.)

有没有理由不删除ContextLoaderListener

java spring servlets dependency-injection

122
推荐指数
3
解决办法
5万
查看次数

如何在Spring中将依赖项注入自我实例化的对象?

假设我们有一个班级:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}
Run Code Online (Sandbox Code Playgroud)

然后我们创建了这个类的对象(或者其他一些框架创建了这个类的实例).

MyClass obj = new MyClass();
Run Code Online (Sandbox Code Playgroud)

是否仍然可以注入依赖项?就像是:

applicationContext.injectDependencies(obj);
Run Code Online (Sandbox Code Playgroud)

(我认为Google Guice有这样的东西)

java spring dependency-injection

120
推荐指数
4
解决办法
7万
查看次数

带@Value的Spring表达式语言(SpEL):美元与哈希($ vs.#)

我什么时候使用就有点糊涂了${...}比较#{...}.Spring的文档仅使用#{...},但有很多示例使用${...}.此外,当我开始使用SpEL时,我被告知要使用${...}它并且工作正常.

对于那些困惑的人来说,我将如何使用它

@Component
public class ProxyConfiguration {

    @Value("${proxy.host}")
    private String host;
    @Value("${proxy.port}")
    private String port;

    :
}
Run Code Online (Sandbox Code Playgroud)

和一些属性文件:

proxy.host=myproxy.host
proxy.port=8000
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • 有什么不同或是否相同?
  • 是不推荐使用的一个版本,所以我应该使用另一个版本?

spring dependency-injection spring-el

120
推荐指数
2
解决办法
4万
查看次数

有没有另类的混蛋注射?(AKA穷人通过默认构造函数注入)

在一些情况下,我最常使用"混蛋注射".当我有一个"适当的"依赖注入构造函数时:

public class ThingMaker {
    ...
    public ThingMaker(IThingSource source){
        _source = source;
    }
Run Code Online (Sandbox Code Playgroud)

但是,对于我打算作为公共API(其他开发团队将使用的类)的类,我永远找不到比编写具有最可能需要的依赖项的默认"bastard"构造函数更好的选择:

    public ThingMaker() : this(new DefaultThingSource()) {} 
    ...
}
Run Code Online (Sandbox Code Playgroud)

这里明显的缺点是,这会对DefaultThingSource产生静态依赖; 理想情况下,没有这种依赖性,消费者总是会注入他们想要的任何IThingSource.但是,这太难用了; 消费者希望新建一个ThingMaker并开始制作物品,然后几个月后,在需要时注入其他东西.在我看来,这只留下了几个选项:

  1. 省略这个混蛋构造函数; 迫使ThingMaker的消费者理解IThingSource,了解ThingMaker如何与IThingSource交互,查找或编写具体类,然后在构造函数调用中注入实例.
  2. 省略bastard构造函数并提供单独的工厂,容器或其他引导类/方法; 以某种方式让消费者明白他们不需要编写自己的IThingSource; 迫使ThingMaker的消费者找到并理解工厂或引导程序并使用它.
  3. 保持bastard构造函数,使消费者能够"新建"一个对象并使用它运行,并处理DefaultThingSource上的可选静态依赖.

男孩,#3肯定看起来很有吸引力.还有另一种更好的选择吗?#1或#2似乎不值得.

.net c# oop constructor dependency-injection

116
推荐指数
5
解决办法
1万
查看次数

Ioc/DI - 为什么我必须在应用程序的入口点引用所有层/组件?

(与此问题相关,EF4:为什么在启用延迟加载时必须启用代理创建?).

我是DI的新手,所以请耐心等待.我知道容器负责实例化我所有已注册的类型,但为了做到这一点,它需要引用我的解决方案中的所有DLL及其引用.

如果我没有使用DI容器,我就不必在我的MVC3应用程序中引用EntityFramework库,只需引用我的业务层,它将引用我的DAL/Repo层.

我知道在一天结束时所有的DLL都包含在bin文件夹中,但我的问题是必须通过VS中的"添加引用"显式引用它,以便能够发布包含所有必需文件的WAP.

dependency-injection castle-windsor inversion-of-control

114
推荐指数
3
解决办法
2万
查看次数

InvalidOperationException:无法解析类型为"Microsoft.AspNetCore.Http.IHttpContextAccessor"的服务

我开始将我的asp.net核心RC1项目转换为RC2,并面临现在IHttpContextAccessor无法解决的问题.

为简单起见,我使用Visual Studio模板创建了新的ASP.NET RC2项目ASP.NET Core Web Application (.Net Framework).比我添加了HomeController的构造函数,为我创建了哪个模板.

public HomeController(IHttpContextAccessor accessor)
{
}
Run Code Online (Sandbox Code Playgroud)

在我开始申请后,我收到下一个错误:

InvalidOperationException:尝试激活"TestNewCore.Controllers.HomeController"时,无法解析类型"Microsoft.AspNetCore.Http.IHttpContextAccessor"的服务.Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp,Type type,Type requiredBy,Boolean isDefaultParameterRequired)

在我的实际应用中,我需要解决IHttpContextAccessor我自己的服务类获得访问_contextAccessor.HttpContext.Authentication_contextAccessor.HttpContext.User.在RC1中,Everething运行良好.那么怎么能想到RC2呢?

.net dependency-injection asp.net-core

114
推荐指数
5
解决办法
5万
查看次数

Spring @Autowire on Properties vs Constructor

因为我一直在使用Spring,如果我要编写一个具有依赖项的服务,我会执行以下操作:

@Component
public class SomeService {
     @Autowired private SomeOtherService someOtherService;
}
Run Code Online (Sandbox Code Playgroud)

我现在遇到了使用另一个约定来实现相同目标的代码

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}
Run Code Online (Sandbox Code Playgroud)

我理解这两种方法都有效.但是使用选项B有一些优势吗?对我来说,它在类和单元测试中创建了更多代码.(必须编写构造函数而不能使用@InjectMocks)

有什么我想念的吗?除了在单元测试中添加代码之外,还有其他任何自动装配的构造函数吗?这是一种更优先的依赖注入方式吗?

spring constructor dependency-injection autowired

111
推荐指数
7
解决办法
4万
查看次数