Visual Studio 2017代码覆盖率报告异步方法的部分覆盖率

时间:2019-03-01 04:28:44

标签: c# unit-testing visual-studio-2017 code-coverage xunit

我正在为一个简单的asp.net核心中间件进行小型单元测试,并尝试确定是否可以在这种非常基本的情况下获得100%的覆盖率。我正在使用Visual Studio 2017>“分析代码覆盖率”,xUnit和Moq以确保完整性。在我的异步方法(如下所示)上,代码分析仅报告部分覆盖。有没有办法完全覆盖这些内容?

//示例中间件

internal sealed partial class JsonExceptionMiddleware
{
    private const string DefaultErrorMessage = "A server error occurred.";
    private readonly RequestDelegate _next;
    private readonly ILogger<JsonExceptionMiddleware> _logger;


    public JsonExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
        _logger = loggerFactory?.CreateLogger<JsonExceptionMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));

        IncludeExceptionMessage = hostingEnvironment.IsDevelopment();
        IncludeExceptionStackTrace = hostingEnvironment.IsDevelopment();
    }


    /// <summary>
    /// Gets or sets whether the <see cref="Exception.StackTrace"/> should be included in the response message.
    /// </summary>
    public bool IncludeExceptionStackTrace { get; set; }

    /// <summary>
    /// Gets or sets whether the <see cref="Exception.Message"/> should be included in the response message.
    /// </summary>
    public bool IncludeExceptionMessage { get; set; }

    /// <summary>
    /// Implements the <see cref="RequestDelegate"/> so this class can be used as middleware.
    /// </summary>
    /// <param name="context">The current <see cref="HttpContext"/>.</param>
    /// <returns>A <see cref="Task"/> that completes when the error message is flush to the HTTP response.</returns>
    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            if (context.Response.HasStarted) throw;

            context.Response.Clear();
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Response.ContentType = ApiConstants.Http.JsonContentType;

            ApiError error = BuildError(ex);

            await context.Response.WriteAsync(JsonConvert.SerializeObject(error, new JsonSerializerSettings(){ NullValueHandling = NullValueHandling.Ignore}));
        }
    }

    private ApiError BuildError(Exception ex)
    {
        string message = DefaultErrorMessage;
        string detail = null;
        string stack = null;

        if (IncludeExceptionMessage)
            detail = ex.Message;

        if (IncludeExceptionStackTrace)
            stack = ex.StackTrace;

        var error = new ApiError(message, detail, stack);
        return error;
    }
}

蓝色=已覆盖,黄色=部分覆盖,红色=未覆盖

enter image description here

enter image description here

//样品单位测试

    [Fact]
    public async Task SampleUnit()
    {
        // arrange

        var environment = new Mock<IHostingEnvironment>();
        environment
            .SetupGet(x => x.EnvironmentName)
            .Returns(EnvironmentName.Development);

        var response = new Mock<HttpResponse>();
        response
            .Setup(x => x.HasStarted)
            .Returns(true);

        var httpContext = new Mock<HttpContext>();
        httpContext
            .SetupGet(x => x.Response)
            .Returns(response.Object);

        var loggerFactory = new Mock<LoggerFactory>();

        var jsonExceptionMiddleware = new JsonExceptionMiddleware((innerHttpContext) => throw new Exception(SampleExceptionDetail), loggerFactory.Object, environment.Object);

        // act & assert

        await Assert.ThrowsAsync<Exception>(async () => await jsonExceptionMiddleware.Invoke(httpContext.Object).ConfigureAwait(false));
    }

1 个答案:

答案 0 :(得分:1)

从所涵盖的代码的外观来看,测试会抛出等待状态,并且只会流过catch块。

通过不向请求委托中引发异常,使await顺利完成。使用提供的样本测试,您需要像这样初始化中间件

//...

var jsonExceptionMiddleware = new JsonExceptionMiddleware((context) => Task.CompletedTask, 
    loggerFactory.Object, environment.Object);

//...

对于其他未发现的代码,您只需确保像现在一样在等待中抛出错误,但是请确保context.Response.HasStartedtrue