如何对异常处理程序中间件进行单元测试

2024-03-03

我正在尝试使用自定义错误处理程序为我的 .NET Core 3 API 返回格式正确的异常。处理程序工作得很好,我遇到的问题是编写适当的单元测试来测试处理程序。我为此注册了中间件,如下所示:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IEnvService envService)
    {
        if (envService.IsNonProductionEnv())
        {
            app.UseExceptionHandler("/Error-descriptive");
        }
        else
        {
            app.UseExceptionHandler("/Error");
        }

        ...

     }

以下是“错误描述”端点的代码:

    public IActionResult ErrorDescriptive()
    {
        if (!_env.IsNonProductionEnv())            
            throw new InvalidOperationException("This endpoint cannot be invoked in a production environment.");

        IExceptionHandlerFeature exFeature = HttpContext.Features.Get<IExceptionHandlerFeature>();

        return Problem(detail: exFeature.Error.StackTrace, title: exFeature.Error.Message);
     }

我具体处理的问题是这一行:

IExceptionHandlerFeature exFeature = HttpContext.Features.Get<IExceptionHandlerFeature>();

从单元测试的角度来看,我很难让它工作,因为(据我所知)这行代码从服务器获取了最新的异常。有没有一种方法可以在我的测试中设置从 HttpContext 获得的异常?另外,由于这是使用 HttpContext,我是否还需要合并它的模拟版本,例如 DefaultHttpContext?


您可以通过注入来实现这一点IHttpContextAccessor https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor?view=aspnetcore-3.1控制器中的接口。 该接口提供了访问 HttpContext 对象的抽象,主要是在 Web 库之外。

要使用它,你必须services.AddHttpContextAccessor();在您的启动文件中。 然后你可以将它注入到你的控制器中。

[ApiController]
public class ErrorController
{
    private readonly ILogger<ErrorController> logger;
    private readonly IHttpContextAccessor httpContextAccessor;

    public ErrorController(ILogger<ErrorController> logger, IHttpContextAccessor httpContextAccessor){
        this.logger = logger;
        this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }
}

然后在您的方法中,您使用访问器接口中的 HttpContext 而不是基类。

[Route("/error-development")]
public IActionResult HandleErrorDevelopment() 
{
     var exceptionFeature = this.httpContextAccessor.HttpContext.Features.Get<IExceptionHandlerFeature>();
     var error = exceptionFeature.Error;
}

由于我们现在正在针对注入的接口进行工作,因此您可以在单元测试中模拟它。请记住,您必须模拟您正在调用的整个链。 (我在本例中使用 xUnit 和 Moq)。

public ErrorControllerTests() {
     this.httpContextAccessorMock = new Mock<IHttpContextAccessor>();
     this.httpContextMock = new Mock<HttpContext>();
     this.featureCollectionMock = new Mock<IFeatureCollection>();
     this.exceptionHandlerFeatureMock = new Mock<IExceptionHandlerFeature>();

     this.httpContextAccessorMock.Setup(ca => ca.HttpContext).Returns(this.httpContextMock.Object);
     this.httpContextMock.Setup(c => c.Features).Returns(this.featureCollectionMock.Object);
     this.featureCollectionMock.Setup(fc => fc.Get<IExceptionHandlerFeature>()).Returns(this.exceptionHandlerFeatureMock.Object);

     this.controller = new ErrorController(null, this.httpContextAccessorMock.Object);
}

[Fact]
public void HandleErrorDevelopment() {
    Exception thrownException;
    try{
        throw new ApplicationException("A thrown application exception");
    }
    catch(Exception ex){
        thrownException = ex;
    }
    this.exceptionHandlerFeatureMock.Setup(ehf => ehf.Error).Returns(thrownException);

    var result = this.controller.HandleErrorDevelopment();
    var objectResult = result as ObjectResult;
    Assert.NotNull(objectResult);
    Assert.Equal(500, objectResult.StatusCode);
    var problem = objectResult.Value as ProblemDetails;
    Assert.NotNull(problem);
    Assert.Equal(thrownException.Message, problem.Title);
    Assert.Equal(thrownException.StackTrace, problem.Detail);
}

重要的: 您不能使用ControllerBase.Problem()方法。该实现依赖于ControllerBase.HttpContext检索一个ProblemDetailsFactory object.

您可以在源代码 https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ControllerBase.cs。由于您的单元测试没有设置上下文,因此它会抛出NullReferenceException.

Factory只不过是创建一个的帮手ProblemDetails https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-3.1目的。再次你可以看到GitHub 上的默认实现 https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs.

我最终创建了自己的私有方法来创建这个对象。

private ProblemDetails CreateProblemDetails(
     int? statusCode = null,
     string title = null,
     string type = null,
     string detail = null,
     string instance = null){

     statusCode ??= 500;
     var problemDetails = new ProblemDetails
     {
          Status = statusCode.Value,
          Title = title,
          Type = type,
          Detail = detail,
          Instance = instance,
     };

     var traceId = Activity.Current?.Id ?? this.httpContextAccessor.HttpContext?.TraceIdentifier;
     if (traceId != null)
     {
         problemDetails.Extensions["traceId"] = traceId;
     }

     return problemDetails;
}

完整的错误处理方法如下所示。

[Route("/error-development")]
public IActionResult HandleErrorDevelopment() {
      var exceptionFeature = this.httpContextAccessor.HttpContext.Features.Get<IExceptionHandlerFeature>();
      var error = exceptionFeature.Error;
      this.logger?.LogError(error, "Unhandled exception");
      var problem = this.CreateProblemDetails(title: error.Message, detail: error.StackTrace);
       return new ObjectResult(problem){
           StatusCode = problem.Status
       };
}

对于生产,我要么提供要向用户显示的通用消息,要么在已经有要显示的用户友好消息的代码中抛出自定义处理的异常。

if (error is HandledApplicationException)
{
    // handled exceptions with user-friendly message.
    problem = this.CreateProblemDetails(title: error.Message);
}
else {
    // unhandled exceptions. Provice a generic error message to display to the end user.
    problem = this.CreateProblemDetails(title: "An unexpected exception occured. Please try again or contact IT support.");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何对异常处理程序中间件进行单元测试 的相关文章

  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 如何模拟从抽象类继承的受保护子类方法?

    如何使用 Mockito 或 PowerMock 模拟由子类实现但从抽象超类继承的受保护方法 换句话说 我想在模拟 doSomethingElse 的同时测试 doSomething 方法 抽象超类 public abstract clas
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 忽略重复条目并在 EF Core 中的 DbContext.SaveChanges() 上提交成功条目

    我有一个 ASP Net Core 2 2 Web API 在我的一个控制器操作中 我向 MySQL 数据库表添加了一堆行 我使用的是 Pomelo 例如 dbContext AddRange entities dbContext Save
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 空指针与 int 等价

    Bjarne 在 C 编程语言 中写道 空指针与整数零不同 但 0 可以用作空指针的指针初始值设定项 这是否意味着 void voidPointer 0 int zero 0 int castPointer reinterpret cast
  • Cucumber Java 与 Spring Boot 集成 - Spring @Autowired 抛出 NullPointer 异常

    我正在为 Spring boot 应用程序编写 cucumber java 单元测试来测试每个功能 当我与 Spring Boot 集成时 Autowired 类抛出 NullPointer 异常 Spring Boot应用程序类 Spri
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • 单元测试时 Android Studio 2.0 中测试状态终止且没有任何失败消息

    Issue 我昨天在 Ubuntu 上从 1 5 升级到了 Android Studio 2 0 当我在 Android Studio 2 0 中进行单元测试时 即使所有测试都已通过 它也会显示 终止测试 状态 有时它只显示部分测试通过 我
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么

随机推荐

  • Magento API 的创建发票方法无法正常工作

    我正在尝试使用 XMLRPC 在 Android 应用程序中使用 Magento API 创建销售订单发票 我正在使用方法 sales order invoice create 用于创建发票 此方法在给定数量的响应中为我提供发票 ID 如中
  • 如何通过 API Laravel 处理用户注册

    我一直在阅读和观看一些有关 Laravel API 开发的教程 尽管我使用过一些 Laravel 但我对 API 开发完全是新手 从我读过的所有教程来看 它们处理的是 登录 获取一些数据 更新信息 删除信息 甚至将一些信息插入数据库 我的问
  • C# .NET 中的 Web 响应最多只能运行几次

    我正在使用 twitter api 开发一个应用程序 其中涉及编写一个方法来检查用户是否存在 这是我的代码 public static bool checkUserExists string user string URL https tw
  • 如何在 Struts 2 中向我的所有视图公开一个对象?

    我有一个使用 Struts 2 和 Freemarker 模板以及 Spring 4 的 Web 应用程序 我有一些配置字符串存储在 properties我需要在每个页面上呈现的文件 例如 我们的 CDN 路径 其中包含版本字符串 现在这些
  • OSError:libgdal.dylib:无法打开文件

    问题是 Docker 无法正常运行 因为OSError opt homebrew Cellar gdal 3 3 0 2 lib libgdal dylib cannot open shared object file No such fi
  • 使用 Pyx 绘制大括号

    如何使用 Pyx 在任意两个点之间绘制一条 支撑 线 它看起来像这样 大括号示例http tof canardpc com view d16770a8 0fc6 4e9d b43c a11eaa09304d http tof canardp
  • 如何编写一个以两个矩阵 A 和 B 作为输入并输出乘积矩阵 A*B 的函数?

    如何编写一个以两个矩阵 A 和 B 作为输入并输出乘积矩阵 A B 的函数 使用 MATLAB 带有循环和条件 我的尝试 function prodAB MultiplicoMatrices A B prod 0 prodAB for i
  • 发生特定情况时如何停止 Kotlin 流程

    如果代码中发生某些情况 我想取消 kotlin 流程 假设我有一个方法如下 fun test Flow
  • 自定义设备控制器不工作

    我有两个模型居民和用户 它们都包含 roll number 属性 我现在已经在驻留模型中输入了数据 当用户注册哪个是 Devise 资源时 它会检查驻留模型中是否存在相同的 roll number 然后就可以注册用户了 所以基本上我向 De
  • 替换JS中某个字符的所有实例?

    我正在尝试创建一个简单的函数来替换 JS 中字符串中某个字符的所有实例 在这种情况下 我想替换所有a s with o s 我很确定代码是正确的 但输出仍然是原始字符串 function replaceLetter string for v
  • mongorestore 随机崩溃(致命错误)

    我使用的是 macOS 10 12 mongod version db version v3 2 8 git version ed70e33130c977bda0024c125b56d159573dbaf0 OpenSSL version
  • 如何在源代码中查找搜索词

    我正在寻找一种在项目的 C C 代码中搜索给定术语的方法 同时忽略注释和字符串中出现的任何情况 由于代码库相当大 我正在寻找一种方法自动地识别与我的搜索词匹配的代码行 因为它们需要手动检查 如果可能的话 我想在我的 Linux 系统上执行搜
  • 绘制相同值时显示更大的点

    当我绘制以下示例时 Participant lt c 1 12 AnswersDay1 lt c 9 3 9 13 7 12 10 7 9 0 12 11 Day1Group lt c 0 1 0 1 0 1 0 1 0 1 0 1 Pus
  • 傅立叶空间中的滤波器的行为与预期不同

    这是我提出的已回答问题的后续内容 可以找到here https stackoverflow com questions 54022376 inverse fft returns negative values when it should
  • RDP(VM) 最小化时自动化脚本失败

    我一直面临着在其中一台虚拟机上自动执行脚本的问题 我已经实现了保存文档功能的自动化 该功能最好是 Windows 设计的 UI 我尝试过使用各种技术 工具 如 AutoIT Python Sikuli 但如果虚拟机最小化 脚本就会停止 如果
  • 使用 Savon 在 Ruby on Rails 中进行 SOAP 调用在信封和主要操作方面变得很奇怪

    在使用 Savon rb 的 Rails 项目中 我尝试进行非常复杂的 SOAP 调用 至少复杂到 Savon 构建者太麻烦了 所以我决定直接操作 xml 首先我启动客户端 client Savon client endpoint gt h
  • 有 XHTML 5 验证器吗?

    是否有专门针对 XHTML 5 的验证器 即 HTML 5 的 XML 序列化 这W3C 验证器 http validator w3 org 支持文档类型 HTML 5 experimental which treats as valid
  • 从第二次“应用内购买”开始在 Android 中抛出异常

    我正在尝试包含在应用程序购买中 并且已成功显示可用的 SKU 现在我想进行虚假购买 所以我使用了 appId android test purchased 第一次它工作完美 但从接下来它抛出异常 如下所示 尝试在空对象引用上调用虚拟方法 a
  • 为什么主键顺序很重要?

    我最近在 EntityFramework 项目中设置了一个类 它将其几个成员指定为组合键 但是 当需要从中创建数据库时 它给出了错误 无法确定类型 NNNNN 的复合主键排序 使用 ColumnAttribute 或 HasKey 方法指定
  • 如何对异常处理程序中间件进行单元测试

    我正在尝试使用自定义错误处理程序为我的 NET Core 3 API 返回格式正确的异常 处理程序工作得很好 我遇到的问题是编写适当的单元测试来测试处理程序 我为此注册了中间件 如下所示 public void Configure IApp