Отчеты о покрытии кода в Visual Studio 2017 Частичное покрытие при асинхронных методах - PullRequest
0 голосов
/ 01 марта 2019

Я работаю над небольшим модульным тестом для простого промежуточного программного обеспечения ядра 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 голосов
/ 01 марта 2019

Судя по приведенному коду, тесты запускают await и проходят только через блок catch.

Позволяют await выполняться до конца, не выбрасывая исключение взапрос делегата.Используя предоставленный пример теста, вам нужно будет инициализировать промежуточное программное обеспечение следующим образом:

//...

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

//...

Для другого непокрытого кода вам просто нужно убедиться, что в ожидании возникла ошибка, как сейчас, но сделайтеуверен, что context.Response.HasStarted это true.

...