如何使用 MOQ 对象测试 Ninject ConstructorArguments?

2024-03-10

我最近一直在做我的第一个测试驱动开发项目,并一直在学习 Ninject 和 MOQ。这是我第一次尝试这一切。我发现 TDD 方法发人深省,Ninject 和 MOQ 也很棒。我正在从事的项目并不是特别适合 Ninject,因为它是一个高度可配置的 C# 程序,旨在测试 Web 服务接口的使用。

我已将其分解为模块并在整个商店中提供接口,但我仍然发现在从 Ninject 内核获取服务的实现时必须使用大量构造函数参数。例如;

在我的 Ninject 模块中;

Bind<IDirEnum>().To<DirEnum>()

我的 DirEnum 类;

public class DirEnum : IDirEnum
{
    public DirEnum(string filePath, string fileFilter, 
        bool includeSubDirs)
    {
        ....

在我的配置器类(这是主入口点)中,它将所有服务挂钩在一起;

class Configurator
{

    public ConfigureServices(string[] args)
    {
        ArgParser argParser = new ArgParser(args);
        IDirEnum dirEnum = kernel.Get<IDirEnum>(
            new ConstructorArgument("filePath", argParser.filePath),
            new ConstructorArgument("fileFilter", argParser.fileFilter),
            new ConstructorArgument("includeSubDirs", argParser.subDirs)
        );

filePath、fileFilter 和 includeSubDirs 是程序的命令行选项。到目前为止,一切都很好。然而,作为一个认真负责的人,我有一个涵盖这段代码的测试。我想使用最小起订量对象。我为我的测试创建了一个 Ninject 模块;

public class TestNinjectModule : NinjectModule
{
    internal IDirEnum mockDirEnum {set;get};
    Bind<IDirEnum>().ToConstant(mockDirEnum);
}

在我的测试中我这样使用它;

[TestMethod]
public void Test()
{
    // Arrange
    TestNinjectModule testmodule = new TestNinjectModule();
    Mock<IDirEnum> mockDirEnum = new Mock<IDirEnum>();
    testModule.mockDirEnum = mockDirEnum;
    // Act
    Configurator configurator = new Configurator();
    configurator.ConfigureServices();
    // Assert

    here lies my problem! How do I test what values were passed to the
    constructor arguments???

所以上面显示了我的问题。如何测试传递给模拟对象的 ConstructorArguments 的参数是什么?我的猜测是,在这种情况下,Ninject 正在分配 ConstructorArguments,因为 Bind 不需要它们?我可以使用 MOQ 对象对此进行测试,还是需要手动编写一个实现 DirEnum 并接受并“记录”构造函数参数的模拟对象?

注:这段代码是“示例”代码,即我没有逐字复制我的代码,但我认为我已经表达了足够的内容来希望传达问题?如果您需要更多背景信息,请询问!

感谢您的关注。温柔一点,这是我第一次;-)

Jim


您设计应用程序的方式存在一些问题。首先,您直接从代码中调用 Ninject 内核。这被称为服务定位器模式 http://en.wikipedia.org/wiki/Service_locator_pattern and 它被认为是一种反模式 http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx。它使测试您的应用程序变得更加困难,并且您已经经历过这一点。您试图在单元测试中模拟 Ninject 容器,这使事情变得非常复杂。

接下来,您将注入原始类型(string, bool)在你的构造函数中DirEnum类型。我喜欢 MNrydengren 在评论中的表述:

获取“编译时”依赖项 通过构造函数参数和 通过方法的“运行时”依赖关系 参数

我很难猜测该类应该做什么,但是由于您将这些在运行时更改的变量注入到DirEnum构造函数,您最终会得到一个难以测试的应用程序。

有多种方法可以解决这个问题。我想到的两个是方法注入的使用和工厂的使用。哪一种可行取决于您。

使用方法注入,你的Configurator类将如下所示:

class Configurator
{
    private readonly IDirEnum dirEnum;

    // Injecting IDirEnum through the constructor
    public Configurator(IDirEnum dirEnum)
    {
        this.dirEnum = dirEnum;
    }

    public ConfigureServices(string[] args)
    {
        var parser = new ArgParser(args);

        // Inject the arguments into a method
        this.dirEnum.SomeOperation(
            argParser.filePath
            argParser.fileFilter
            argParser.subDirs);
    }
}

使用工厂,您需要定义一个知道如何创建新的工厂IDirEnum types:

interface IDirEnumFactory
{
    IDirEnum CreateDirEnum(string filePath, string fileFilter, 
        bool includeSubDirs);
}

Your Configuration类现在可以依赖于IDirEnumFactory界面:

class Configurator
{
    private readonly IDirEnumFactory dirFactory;

    // Injecting the factory through the constructor
    public Configurator(IDirEnumFactory dirFactory)
    {
        this.dirFactory = dirFactory;
    }

    public ConfigureServices(string[] args)
    {
        var parser = new ArgParser(args);

        // Creating a new IDirEnum using the factory
        var dirEnum = this.dirFactory.CreateDirEnum(
            parser.filePath
            parser.fileFilter
            parser.subDirs);
    }
}

查看这两个示例中如何将依赖项注入到Configurator班级。这被称为依赖注入模式 http://en.wikipedia.org/wiki/Dependency_injection,与服务定位器模式相反,其中Configurator通过调用 Ninject 内核来请求其依赖项。

现在,自从你的Configurator完全不受任何 IoC 容器的影响,您现在可以通过注入所需依赖项的模拟版本来轻松测试此类。

剩下的就是在应用程序顶部配置 Ninject 容器(用 DI 术语来说:成分根 https://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection)。对于方法注入示例,您的容器配置将保持不变,对于工厂示例,您将需要替换Bind<IDirEnum>().To<DirEnum>()符合以下内容:

public static void Bootstrap()
{
    kernel.Bind<IDirEnumFactory>().To<DirEnumFactory>();
}

当然,您需要创建DirEnumFactory:

class DirEnumFactory : IDirEnumFactory
{
    IDirEnum CreateDirEnum(string filePath, string fileFilter, 
        bool includeSubDirs)
    {
        return new DirEnum(filePath, fileFilter, includeSubDirs);
    }        
}

WARNING:请注意,工厂抽象在大多数情况下并不是最好的设计,正如所解释的那样here https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=100.

您需要做的最后一件事是创建一个新的Configurator实例。您可以简单地执行以下操作:

public static Configurator CreateConfigurator()
{
    return kernel.Get<Configurator>();
}

public static void Main(string[] args)
{
    Bootstrap():
    var configurator = CreateConfigurator();

    configurator.ConfigureServices(args);
}

这里我们称之为内核。尽管应该防止直接调用容器,但应用程序中始终至少有一个地方可以调用容器,因为它必须连接所有内容。然而,我们尝试最小化直接调用容器的次数,因为它提高了代码的可测试性。

看看我如何没有真正回答你的问题,但展示了一种非常有效地解决问题的方法。

您可能仍想测试您的 DI 配置。在我看来,这是非常有效的。我在我的应用程序中这样做。但为此,您通常不需要 DI 容器,或者即使您需要,这并不意味着您的所有测试都应该依赖于该容器。这种关系只应存在于测试 DI 配置本身的测试中。这是一个测试:

[TestMethod]
public void DependencyConfiguration_IsConfiguredCorrectly()
{
    // Arrange
    Program.Bootstrap();

    // Act
    var configurator = Program.CreateConfigurator();

    // Assert
    Assert.IsNotNull(configurator);
}

该测试间接依赖于 Ninject,当 Ninject 无法构造新的测试时,该测试将会失败Configurator实例。当您保持构造函数不包含任何逻辑并且仅将其用于将获取的依赖项存储在私有字段中时,您可以运行它,而无需承担调用数据库、Web 服务或其他任何内容的风险。

我希望这有帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 MOQ 对象测试 Ninject ConstructorArguments? 的相关文章

  • 使用 Unity 跨多种类型注入相同的 DataContext 实例

    假设我有 IRepository 接口及其实现 SqlRepository 它将 LINQ to SQL DataContext 作为参数 假设我有 IService 接口及其实现服务 它需要三个 IRepository IReposito
  • 将 Mockito 模拟注入 Spring bean

    我想将 Mockito 模拟对象注入到 Spring 3 bean 中 以便使用 JUnit 进行单元测试 我的 bean 依赖项当前是通过使用注入的 Autowired私有成员字段上的注释 我考虑过使用ReflectionTestUtil
  • 在Application_Start中访问ninject内核

    我正在使用 Ninject 和随 nuget 安装的 MVC3 扩展 我的内核设置代码位于 App Start NinjectMVC3 cs 文件中 控制器中的一切都运行良好 但我无法弄清楚如何 正确 绑定 Global asax cs M
  • 在 AngularJS 中覆盖运行时的依赖关系

    我有一个服务叫 doggedHttp 它公开了与 http 现在我想创建一个 doggedResource服务是有角度的 resource服务之上 doggedHttp代替 http 换句话说我想注入 doggedHttp as the h
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • Moq - 是否可以在不使用 It.IsAny 的情况下设置模拟

    我一直使用 Moq 进行单元测试 有时我会嘲笑有很多参数的方法 想象一下这样的方法 public class WorkClient public void DoSomething string itemName int itemCount
  • 测试 ASP.NET Core IMemoryCache 的正确方法

    我正在编写一个简单的测试用例 用于测试我的控制器在调用我的服务之前是否调用缓存 我正在使用 xUnit 和 Moq 来完成该任务 我遇到了一个问题 因为GetOrCreateAsync
  • 依赖注入系统中的事件朝哪个方向发展?

    上或下 我是一个非常注重视觉的人 我将我的应用程序视为一个层次结构 顶部是根 底部是叶子 我还了解到 在 DI 系统中 容器不知道其所包含对象的职责 功能 相反 所包含的对象知道它们的上下文 因为上下文 依赖项 被注入 UP 非 DI 方式
  • 测试 - 存根服务方法未定义

    我已经在非常简单的代码上编写了一个非常简单的测试 但由于某种原因存根服务方法未定义 当我使用 Jasmine Spy 时 它可以工作 但对于这样一个简单的任务 有人可以解释一下为什么会发生这种情况吗 我删除了 import 语句只是为了减少
  • RubyMine 不能使用 Guard 吗?

    由于某些无法解释的原因 RubyMine 会自动保存您所做的每一个更改 因此每次击键都会触发 Guard 运行您的测试 最可笑的是 显然没有办法禁用这个自动保存 功能 我只是想知道 RubyMine 似乎是 Rails 开发人员中非常流行的
  • Ruby 依赖注入库

    我一直在研究一些 Ruby 依赖注入库 特别是 我检查了Needle http needle rubyforge org and Copland http copland rubyforge org 它们已经存在很长一段时间了 但用途并不多
  • Dagger2 - 项目重建错误 - 字段注入 - Android

    我一直在尝试实现 Dagger2 Problem 当我使用构造函数注入时 它工作正常 但是当我使用字段注入时 它会抛出如下错误 Error 6 48 error cannot find symbol class DaggerApplicat
  • 这是依赖注入吗?这是一种不好的做法吗?

    我有一个小框架 我是这样编码的 我不确定这是否称为依赖注入 我不知道它是否像设计模式 我也不知道并且想知道是否通过 this因为 param 是一种不好的做法 看看这个 不是一个有效的示例 只是将这些代码写入浏览器中以供解释 This is
  • AngularJS - 服务、工厂、过滤器等中的依赖注入

    因此 我想在我的 Angular 应用程序中使用一些插件和库 目前 我只是引用这些函数 方法 因为它们是在 99 的应用程序中以完全忽略依赖注入的方式使用的 我有 例如 javascript 库 MomentJS 它处理格式化和验证日期 并
  • 起订量和代码合同

    当使用类不变量时 代码契约似乎到处注入代码 像这样的东西 ContractClassFor typeof IX interface IXContract ClassInvariant void Invariant ContractClass
  • 依赖注入错误:在注册类时尝试激活时无法解析类型的服务

    我创建了一个 NET Core MVC 应用程序 并使用依赖注入和存储库模式将存储库注入到我的控制器中 但是 我收到错误 InvalidOperationException 尝试激活 WebApplication1 Controllers
  • 如何调试symfony2服务容器中配置的标签和服务?

    我正在编写一个服务来处理 AccessDeniedException 并且我找到了一种方法来解决它使用 Symfony2 的 AccessDeniedHandlerInterface https stackoverflow com ques
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 如何使用温莎城堡注册通用装饰器?

    我需要装饰一切基于ICommandHandler
  • 为什么在 CDI 中使用构造函数而不是 setter 注入?

    我在这里找不到任何合理的答案 所以我希望它不是重复的 那么为什么我应该更喜欢 setter 或构造函数注入而不是简单的注入 Inject MyBean bean 如果您需要在类初始化期间对注入的 bean 执行某些操作 我会使用构造函数注入

随机推荐

  • 为什么 getLocationOnScreen(location) 总是返回 0?

    In my FragmentLayout我有一个LinearLayout具有多个子视图 TextView CardView 我想找到所有的顶部偏移量LinearLayout意见但我总是得到零 这是我的代码 Override public V
  • 部分多键映射的数据结构?

    我的数据由映射到值的键组成 如下所示 Key Value 0 0 0 0 a 0 0 0 1 b 0 1 0 1 c 0 1 1 0 d 我正在寻找一种可以有效地对键执行搜索查询的数据结构 其中查询可以是完整或部分指定键 例如 0 0 0
  • 从 WP7 上的 xna 游戏启动 Internet Explorer

    我想知道是否可以从 Windows Phone 7 上运行的 XNA 游戏启动 Internet Explorer 我想将我的玩家重定向到我的网站 此致 我相信您仍然可以使用Web浏览器任务 http msdn microsoft com
  • ClojureScript 地图查找速度慢

    我有一个简单的地图 def my map 1 2 3 1 1 2 4 5 3 4 2 3 4 5 3 3 5 2 5 6 9 2 1 5 8 3 1 6 我用来执行查找的 然而 这表现得相当差 time doseq x range 500
  • Webpack 长期缓存

    Scenario 我正在尝试使用 webpack 将我的供应商脚本与我的应用程序脚本分开捆绑 尝试1 index js var require lodash console log webpack config js var path re
  • 如何在没有 Django 其余部分的情况下使用 Django 模板?

    我想在我的 Python 代码中使用 Django 模板引擎 但我没有构建基于 Django 的网站 如果没有 settings py 文件 和其他文件 并且必须设置 DJANGO SETTINGS MODULE 环境变量 如何使用它 如果
  • 如何获得 Java 软件商业许可? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我用 Java 编写了一个基于桌面的会计软件 我想放置密钥或许可证或其他类型的安全措施来防止客户端重新分发软件 我只是想知道该怎么办
  • 当对三个整数使用两次 = 运算符时,会发生什么? [复制]

    这个问题在这里已经有答案了 这是做什么的 int x 1 int y 2 int z 3 x y z 我在一些开源项目中遇到过多种此类事情 并且一直对它们感到困惑 我的意思是 运算符在三个整数上使用两次 即x y z 我需要理解这一点 谢谢
  • Visual Studio 无法在 Windows 10 上正确安装

    我将我的笔记本电脑升级到 Windows 10 无预览版 真正的东西 然后我安装了 Visual Studio Community Edition 2015 我已经安装了 VS 2013 这样我就可以开始使用 Cordova 功能 安装过程
  • 使用 p 值的逐步回归删除 p 值不显着的变量

    我想表演一个逐步线性回归 using p values作为选择标准 例如 在每个步骤中删除具有最高即最不显着 p 值的变量 当所有值均由某个阈值定义的显着时停止alpha 我完全知道我应该使用 AIC 例如命令step or stepAIC
  • 获取当前function()作用域的所有变量

    我有问题 我想获取当前的函数范围 我有这个示例代码 我工作正常 function nittle var Pen new Dot Generated dynamical through eval for key in window if wi
  • 使用 Boto3 从 S3 下载文件夹

    Using Boto3 Python SDK 我能够使用该方法下载文件bucket download file 有没有办法下载整个文件夹 Answer recommended by AWS collectives aws Collectiv
  • Python:如何使用 xlwt 编写复数到 Excel?

    我正在尝试使用以下命令将 Python 列表写入 Excel 文件xlwt图书馆 import xlwt from tempfile import TemporaryFile book xlwt Workbook sheet1 book a
  • 调用静态 JNI 方法从 C++ 返回字符串

    我正在尝试在Android中调用以下java方法 public static String getLevelFile String levelName body 从 C 使用以下 jni 代码 JniMethodInfoJavaApi me
  • 调用window.location.href后如何执行脚本?

    我有一个脚本可以将用户重定向到另一个页面 我想在新页面完全加载后将一些内容加载到新页面上的 div 中 我怎样才能做到这一点 以下不起作用 function goToPage window location href http www my
  • 如何在bash中测试变量是否以字符串开头?

    对于固定的前缀长度 我可以这样做 a filename 0 2 a echo temporary emacs file 对于任意前缀如何做到这一点 有更干净的方法吗 s 运算符在右操作数中采用模式 var 123 1234 var
  • 包设置不会传播到分布式的工作人员

    Info julia version julia version 1 6 0 lscpu root MyPackage lscpu Architecture x86 64 CPU op mode s 32 bit 64 bit Byte O
  • 图像具有绝对路径 - 如何在 Laravel Mix 中使用子目录 URL

    我的 Laravel Mix 应用程序将放置在服务器上的子目录中 例如 http localhost pat os server public http localhost pat os server public 我的 vue 组件中的图
  • C# 中 if 语句的性能 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我只是想确定在具有大量迭代的循环中使用每个 if 语句对 C 应用程序性能的影响 我还没有找到与此相关的主题 因此我创建了这个主题 对于测试
  • 如何使用 MOQ 对象测试 Ninject ConstructorArguments?

    我最近一直在做我的第一个测试驱动开发项目 并一直在学习 Ninject 和 MOQ 这是我第一次尝试这一切 我发现 TDD 方法发人深省 Ninject 和 MOQ 也很棒 我正在从事的项目并不是特别适合 Ninject 因为它是一个高度可