如何在单元测试中调用依赖注入类方法?

2023-11-24

我是单元测试和 DI 的新手,无法找到一种简单的方法来调用使用依赖项注入设计的类中的方法。

这是我的班级

public class AgentProvisioningServiceHelpher : IAgentProvisioningServiceHelpher
{
    private readonly IExcelParser _excelParser;
    private readonly SupervisorDbContext _SupervisorDbContext;
    private readonly SchedulerNoTrackingDbContext _SchedulerDbContext;

    // constructor
    public AgentProvisioningServiceHelpher(IExcelParser excelParser, SupervisorDbContext supervisorDbContext, SchedulerNoTrackingDbContext SchedulerDbContext)
    {
        _excelParser = excelParser;
        _SupervisorDbContext = supervisorDbContext;
        _SchedulerDbContext = SchedulerDbContext;
    }

    // Function that I want to call in unit test
    public int SimpleMethodToTest(int InputId) 
    {
        return InputId + 1;
    } 
}

这是我的界面代码

    public interface IAgentProvisioningServiceHelpher
    {
        int SimpleMethodToTest(int InputId);
    }

这是我的单元测试代码,我使用的是 Xunit

public class UnitTest1
{
    private IAgentProvisioningServiceHelpher _sut; 
    private IExcelParser _excelParser;
    private SupervisorDbContext _DBcontext1;
    private SchedulerNoTrackingDbContext _DBcontext2;

    public UnitTest1(IExcelParser excelParser, SupervisorDbContext DBcontext1, SchedulerNoTrackingDbContext DBcontext2, IAgentProvisioningServiceHelpher sut)
    {
        _excelParser = excelParser;
        _DBcontext1 = DBcontext1;
        _DBcontext2 = DBcontext2;
        _sut = sut;
    }

    [Fact]
    public void SimpleMethodToTest_Shall_ReturnPlus1()
    {
        // Arrange
        int Input_Int = 1;

        // Act
        // I try to tell the interface to map with the class I want to test
        IAgentProvisioningServiceHelpher _sut = new AgentProvisioningServiceHelpher(_excelParser, _DBcontext1, _DBcontext2);

        // Then I try to call the interface method 
        var result = _sut.SimpleMethodToTest(Input_Int);

        // Assert
        Assert.Equal(2, result);
    }
}

当我尝试运行测试时,Visual Studio 抱怨此错误 - 我该如何解决此问题?

UnitTest1.cs 第 33 行

以下构造函数参数没有匹配的装置数据:IExcelParser excelParser、SupervisorDbContext DBcontext1、SchedulerNoTrackingDbContext DBcontext2、IAgentProvisioningServiceHelpher sut


应用 DI 时,您将依赖项的创建推迟到最后负责任的时刻。这意味着您可以尽可能长时间地承担创建依赖项的负担。但在应用程序的某个地方,需要创建这些依赖项。

这个依赖组合的地方称为成分根。对于您正在运行的应用程序,组合根可能是应用程序的Main方法,或者至少在靠近应用程序启动路径的地方。

单元测试中的组合

编写单元测试时,每个单元测试本身都充当组合根。这意味着单元测试本身(或其调用的方法)本身负责它需要测试的类的组成。这意味着作文不再被推迟,也不再被推高。这就是您想要做的:将对象组合的责任推给单元测试框架。

尽管从技术上讲,某些单元测试框架允许您拦截创建测试类的方式,但让测试框架提供依赖项通常没有什么意义,因为单元测试本身需要控制正在创建的确切依赖项。测试不仅知道什么确切的类型依赖项应该是(即通常是某种假实现),但还需要配置这些(假)依赖项或查询它们的结果以断言测试的正确性。

这意味着不要尝试注入依赖项AgentProvisioningServiceHelpher在构造函数内部UnitTest1, the SimpleMethodToTest_Shall_ReturnPlus1方法必须受控。例如:

[Fact]
public void SimpleMethodToTest_Shall_ReturnPlus1()
{
    // Arrange
    int input = 1;
    int expectedResult = 2;

    var sut = new AgentProvisioningServiceHelpher(
        new FakeExcelParser(),
        new FakeSupervisorDbContext(),
        new FakeSchedulerNoTrackingDbContext());

    // Act
    var actualResult = sut.SimpleMethodToTest(input);

    // Assert
    Assert.Equal(expectedResult, actualResult);
}

不幸的是,随着编写的测试越多,在每个单元测试中创建被测类的所有依赖项变得越来越难以维护。在这种情况下,从测试中提取此组合逻辑到辅助方法或辅助类中就成为一个好习惯。在这种情况下,技巧是确保测试仅提供对该特定测试特别感兴趣的依赖项,而将其余部分留空。例如:

[Fact]
public void Parser_should_always_be_called()
{
    // Arrange
    var parser = new FakeExcelParser();

    AgentProvisioningServiceHelpher sut = this.CreateSut(excelParser: parser);

    // Act
    sut.SimpleMethodToTest(0);

    // Assert
    Assert.IsTrue(parser.GotCalled);
}

private AgentProvisioningServiceHelpher CreateSut(
    IExcelParser excelParser = null,
    SupervisorDbContext supervisorDbContext = null,
    SchedulerNoTrackingDbContext schedulerDbContext = null)
{
    return new AgentProvisioningServiceHelpher(
        excelParser ?? new FakeExcelParser(),
        supervisorDbContext ?? new FakeSupervisorDbContext(),
        schedulerDbContext ?? new FakeSchedulerNoTrackingDbContext());
}

在本次测试中,仅ExcelParser已提供,因为在测试期间显式查询了它。其他两个依赖项将由默认(可能是假的)空实现提供CreateSut method.

在这种情况下,CreateSut成为组合根的一部分。

集成测试中的组合

在编写单元测试时,依赖关系通常是手动连接的,如上所示。但是,如果您正在编写集成测试,则测试中涉及的对象数量通常会更大,并且需要类似于生产应用程序中组成的对象结构(有时需要替换一些依赖项)。让单个测试方法或测试类手动重新创建完整的对象结构通常很麻烦,而且容易出错。应用程序对象结构的更改可能会通过许多测试,并且很容易导致系统的维护性很差。

相反,在集成测试期间,通常尝试重用正在运行的应用程序的组合根使用的相同对象组合逻辑。当您使用 DI 容器来组成应用程序的对象图时,这通常意味着重复使用那些相同的 DI 容器注册。

集成测试将重用相同的 DI 容器配置,模拟集成测试运行所需的一些依赖项,解析被测类并调用其方法之一。但是,集成测试仍然无法从外部注入这些依赖项,因为它可能需要对创建的内容进行一些控制。集成测试仍然是它自己的组合根,尽管它将部分对象组合委托给单独的 Composer 类(DI 容器)。

这是集成测试的示例:

[Fact]
public void Some_integration_test()
{
    // Arrange
    int input = 1;
    int expectedResult = 2;

    // Mock object
    var parser = new FakeExcelParser();

    // Create a valid container to resolve object graphs from
    var container = TestBootstrapper.BuildContainer();

    // Configure it especially for this test (note that I'm inventing a
    // DI Container API here. API will very per DI Container)
    container.Replace<IExcelParser>(parser);

    // Resolve the SUT from the DI Container
    var sut = container.Resolve<AgentProvisioningServiceHelpher>();

    // Act
    var actualResult = sut.SimpleMethodToTest(input);

    // Assert
    Assert.Equal(expectedResult, actualResult);
}

此集成测试使用TestBootstrapper可以在集成测试之间共享的类:

public static class TestBootstrapper
{
    public static Container BuildContainer()
    {
        // Request a fully configured DI Container instance from the
        // actual application. This ensures that the integration test
        // runs using the exact same object graphs as the final application.
        var container = RealApplication.Bootstrapper.BuildContainer();

        // Replace dependencies that should never be used during the
        // integration tests.
        container.Replace<IHardDiskFormatter, FakeDiskFormatter>();
        container.Replace<ISmsSender, FakeSmsSender>();
        container.Replace<IPaymentProvider, FakePaymentProvider>();

        return container;
    }
}

这当然与单元测试有很大不同,单元测试具有高度的隔离性。

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

如何在单元测试中调用依赖注入类方法? 的相关文章

随机推荐

  • C++:使用向量/数组优化速度?

    我有一个嵌套的 for 循环结构 现在我在每次迭代开始时重新声明向量 void function n1 n2 bound etc for int i 0 i
  • 将 InputStream 读入 Data 对象

    在 Swift 3 x 中 我们通常使用以下方式处理二进制数据Data 从它你可以生成大多数其他重要的类型 并且它上面有有用的函数 但我如何创建一个Data从一个InputStream 有什么好的办法吗 我找不到好的方法 我们可以围绕不安全
  • 什么编程语言最像英语? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 我主要是一名Python程
  • Bootstrap 4 与远程 Modal

    我无法使用新的 Twitter Bootstrap 版本 Bootstrap 4 alpha 让 Modal 在远程模式下工作 它与 Bootstrap 3 完美配合 使用 Bootstrap 4 我收到弹出窗口 但模型主体未加载 没有进行
  • 实施 Azure 共置缓存

    使用 VS2012 我从 WebRole Properties Caching Tab 添加了缓存功能 其中 它在 web config 中生成了以下 XML
  • 复合视图模型对象导致远程验证失败

    我在这个项目中的一些表单中使用了复合视图模型的模式 它运作得很好 在本例中 我有一个 VendorAddress 视图模型 我在这个项目的几个地方使用了 Address es 因此我制作了可以重复使用的 Address 视图模型对象 然后我
  • Airflow:为每个文件运行 DAG 的正确方法

    我有以下任务需要解决 文件通过端点不定期发送并存储在本地 我需要为每个文件触发 DAG 运行 对于每个文件 将执行相同的任务 总体流程如下 对于每个文件 运行任务 A gt B gt C gt D 正在批量处理文件 虽然这项任务对我来说似乎
  • 鼠标按下。兄弟姐妹 event.targets 上的传播

    我有 2 个具有 绝对位置 的兄弟节点 它们都处理 mousedown 事件 当我单击 div 2 的透明区域 如图所示 时 如何触发 div 1 的处理程序 如果重叠元素是动态的 我认为不可能使用常规事件冒泡来完成此操作 因为所讨论的两个
  • Azure Fluent API 创建 SQL Server 时出错 - 缺少 x-ms-request-id 标头

    我正在尝试使用 Azure Fluent API 创建一个新的 SQL Server https github com Azure azure sdk for net tree Fluent 但我总是得到 Microsoft Rest Az
  • 如何在代码中使用 wsDualHttpBinding 设置 WCF 客户端?

    我需要连接到我编写的 WCF 服务 而无需为我正在编写的客户端应用程序部署 app config 然而 我一直在努力弄清楚如何在代码中从客户端进行设置 据我所知 有人知道我需要做什么才能让它发挥作用吗 我真的很感激 这是我到目前为止得到的代
  • 如何在 Python 中找到正则表达式的所有匹配项?

    在我正在编写的程序中 我让 Python 使用re search 函数在文本块中查找匹配项并打印结果 但是 一旦在文本块中找到第一个匹配项 程序就会退出 在找到所有匹配项之前程序不会停止的情况下 如何重复执行此操作 有一个单独的函数可以做到
  • 根据目标类型的无形状贴图 HList

    我有以下问题 我想将 HList 的项目映射到另一个 HList 但如果 目标 类型是 URL 则源 HList 中的字符串只能转换为 URL val name Stackoverflow val url https stackoverfl
  • 如何通过JQuery ajaxSend事件覆盖成功函数

    我试图覆盖 ajaxsend 事件的成功函数 但它不起作用 这是代码 document ajaxSend function event xhr options console log ajaxSend var tempSuccess opt
  • 如何每两周在周一和周二运行石英时间表?

    我使用以下方式每两周的星期一运行一次计划 ITrigger trigger TriggerBuilder Create StartAt DateBuilder DateOf StartHour StartMinute StartSecond
  • 为什么 Windows 不允许在模拟其他用户时启动 WinSock

    使用我自己的程序或其他程序 如果进程是使用 CreateProcessWithLogonW 或 CreateProcessAsUserW 创建的 则在调用时我无法运行 Winsock 当我创建套接字时它返回此错误 WSAEPROVIDERF
  • 重写 java equals() 方法 - 不起作用?

    我遇到了一个有趣的 而且非常令人沮丧的 问题equals 今天的方法导致我认为经过良好测试的类崩溃并导致一个错误 我花了很长时间才找到它 为了完整起见 我没有使用 IDE 或调试器 只是使用老式文本编辑器和 System out 时间非常有
  • 获取图像输入流的大小

    我需要获取输入流中找到的图像的高度和宽度 这是我所做的 private Boolean testSize InputStream inputStream BitmapFactory Options Bitmp Options new Bit
  • 空查询上的 ALL 运算符 VS Any

    我正在读甲骨文文档在 ANY 和 ALL 运算符上 我很了解它们的用途 除了一件事 它指出 ALL 如果子查询返回零行 则条件计算结果为 TRUE ANY 如果子查询返回零行 则条件计算结果为 FALSE 对我来说这似乎不太符合逻辑 为什么
  • 使用 Tortoise-ORM 在 FastAPI 中进行测试

    我正在尝试编写一些异步测试FastAPI using 乌龟 ORM under Python 3 8但我不断收到相同的错误 见最后 过去几天我一直在试图解决这个问题 但不知何故 我最近在创建测试方面的所有努力都没有成功 我正在关注FastA
  • 如何在单元测试中调用依赖注入类方法?

    我是单元测试和 DI 的新手 无法找到一种简单的方法来调用使用依赖项注入设计的类中的方法 这是我的班级 public class AgentProvisioningServiceHelpher IAgentProvisioningServi