【问题标题】:Unit testing middleware with NUnit and NSubstitute使用 NUnit 和 NSubstitute 进行单元测试中间件
【发布时间】:2018-10-20 23:08:44
【问题描述】:

我在一个 ASP.NET Core 站点中编写了一些中间件,我正在尝试对其进行单元测试,主要是通过关注使用 Moqthis guide

我的问题是找到一个与new DefaultHttpContext() 等效的 NUnit/NSubstitute。替换 HttpContext 会触发中间件,但是它通过了try。我认为这是因为下面引用的问题。 NUnit 是否具有创建真实 HttpContext 的功能,还是我正在寻找更多的基础设施来实现这一点?

我正在向 Invoke 方法发送一个 DefaultHttpContext 实例。在这种情况下,我不能使用模拟的 HttpContext,因为第一个中间件(我们传递给构造函数的 lambda 函数)需要写入响应。因此 HttpResponse 需要是一个没有被模拟的真实对象。

这是我的测试代码

[TestFixture]
public class ExceptionHelperTests
{
    private IErrorRepository errorRepository;
    private ExceptionHandler handler;

    [SetUp]
    public void Setup()
    {
        errorRepository = Substitute.For<IErrorRepository>();
    }

    [Test]
    public async void Given_AnExceptionHappens_Then_ItShouldBeLogged()
    {
        // Arrange
        const string username = "aUser";
        var user = Substitute.For<ClaimsPrincipal>();
        user.Identity.Name.Returns(username);

        handler = new ExceptionHandler(
            next: async (innerHttpContext) =>
            {
                innerHttpContext.User = user;
            },
            repository: errorRepository);

        // Act
        await handler.Invoke(new DefaultHttpContext());

        // Assert
        errorRepository.Received().LogException(Arg.Any<string>(), Arg.Any<Exception>(), Arg.Is(username));
    }
}

这里是 IErrorRepository

public interface IErrorRepository
{
    Exception LogException(string message, Exception ex, string userId);
    void LogMessage(string message, string errorDetail, string userId);
}

这里是中间件(带有简化的 HandleException):

public sealed class ExceptionHandler
{
    private readonly RequestDelegate _next;
    private readonly IErrorRepository repository;

    public ExceptionHandler(RequestDelegate next, IErrorRepository repository)
    {
        _next = next;
        this.repository = repository;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            HandleException(ex, context.User.Identity.Name);
        }
    }

    public void HandleException(Exception ex, string userId)
    {
        repository.LogException("An unhandled exception has occurred.", ex, userId);
    }
}

【问题讨论】:

  • DefaultHttpContext 是 .net 核心的一部分,而不是测试框架。问题是什么?目前的解释还不清楚。
  • 同时避免async void。如果进行 TAP,请进行测试 async Task
  • 您能否也显示正在测试的方法以使问题成为minimal reproducible example
  • 我已经添加了中间件代码。我原本认为它是一个严格的设计没有帮助,但也许我错了。

标签: unit-testing asp.net-core nunit middleware nsubstitute


【解决方案1】:

DefaultHttpContext 只是HttpContext 抽象类的默认实现。

你可以这样做

var HttpContextSub = Substitute.For<HttpContext>();

【讨论】:

  • 这样代入可以设置身份并通过,但不会抛出异常
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-17
  • 2021-12-03
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多