我正在为一个简单的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;
}
}
蓝色=已覆盖,黄色=部分覆盖,红色=未覆盖
//样品单位测试
[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));
}
答案 0 :(得分:1)
从所涵盖的代码的外观来看,测试会抛出等待状态,并且只会流过catch块。
通过不向请求委托中引发异常,使await
顺利完成。使用提供的样本测试,您需要像这样初始化中间件
//...
var jsonExceptionMiddleware = new JsonExceptionMiddleware((context) => Task.CompletedTask,
loggerFactory.Object, environment.Object);
//...
对于其他未发现的代码,您只需确保像现在一样在等待中抛出错误,但是请确保context.Response.HasStarted
是true
。