C# Visual Studio 2017代码覆盖率报告异步方法的部分覆盖率
我正在为一个简单的asp.net核心中间件进行一个小单元测试,并试图确定是否有可能在这个非常基本的场景中获得100%的覆盖率。我正在使用Visual Studio 2017>“分析代码覆盖率”、xUnit和Moq以获得完整性。在我的异步方法(如下所示)中,代码分析只报告部分覆盖。有没有办法完全覆盖这些 //示例中间件C# Visual Studio 2017代码覆盖率报告异步方法的部分覆盖率,c#,unit-testing,visual-studio-2017,code-coverage,xunit,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 DefaultEr
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;
}
}
内部密封部分类JsonExceptionMiddleware
{
private const string DefaultErrorMessage=“发生服务器错误。”;
private readonly RequestDelegate\u next;
专用只读ILogger\u记录器;
公共JsonExceptionMiddleware(RequestDelegate next、ILoggerFactory loggerFactory、IHostingEnvironment hostingEnvironment)
{
_next=next??抛出新ArgumentNullException(nameof(next));
_logger=loggerFactory?.CreateLogger()??抛出新的ArgumentNullException(nameof(loggerFactory));
IncludeExceptionMessage=hostingEnvironment.IsDevelopment();
IncludeExceptionStackTrace=hostingEnvironment.IsDevelopment();
}
///
///获取或设置是否应在响应消息中包含。
///
公共bool IncludeExceptionStackTrace{get;set;}
///
///获取或设置是否应在响应消息中包含。
///
公共bool IncludeExceptionMessage{get;set;}
///
///实现,因此该类可以用作中间件。
///
///电流。
///当错误消息刷新到HTTP响应时完成的。
公共异步任务调用(HttpContext上下文)
{
尝试
{
等待下一步(上下文);
}
捕获(例外情况除外)
{
if(context.Response.HasStarted)抛出;
context.Response.Clear();
context.Response.StatusCode=(int)HttpStatusCode.InternalServerError;
context.Response.ContentType=ApiConstants.Http.JsonContentType;
APIRERROR error=构建错误(ex);
wait context.Response.WriteAsync(JsonConvert.SerializeObject(错误,新的JsonSerializerSettings(){NullValueHandling=NullValueHandling.Ignore}));
}
}
私有APIRROR构建错误(异常ex)
{
字符串消息=DefaultErrorMessage;
字符串详细信息=null;
字符串堆栈=null;
如果(包括异常消息)
细节=例如消息;
如果(包括异常跟踪)
stack=ex.StackTrace;
var error=新的APIRROR(消息、详细信息、堆栈);
返回误差;
}
}
蓝色=覆盖,黄色=部分覆盖,红色=未覆盖
//样本单元测试
[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));
}
[事实]
公共异步任务SampleUnit()
{
//安排
var-environment=newmock();
环境
.SetupGet(x=>x.EnvironmentName)
.Returns(EnvironmentName.Development);
var response=newmock();
响应
.Setup(x=>x.HasStarted)
.返回(真);
var httpContext=new Mock();
httpContext
.SetupGet(x=>x.Response)
.Returns(response.Object);
var loggerFactory=new Mock();
var jsonExceptionMiddleware=新的jsonExceptionMiddleware((innerHttpContext)=>抛出新异常(SampleExceptionDetail)、loggerFactory.Object、environment.Object);
//行动与主张
await Assert.ThrowsAsync(async()=>await jsonExceptionMiddleware.Invoke(httpContext.Object).ConfigureAwait(false));
}
从覆盖代码的外观上看,测试抛出等待,并且只流经catch块
通过不在请求委托中引发异常,允许wait
流程完成。使用提供的示例测试,您需要像这样初始化中间件
//...
var jsonExceptionMiddleware = new JsonExceptionMiddleware((context) => Task.CompletedTask,
loggerFactory.Object, environment.Object);
//...
对于其他未覆盖的代码,您只需确保在等待时像现在一样抛出错误,但请确保
context.Response.HasStarted
为true
编写更多涵盖所有执行路径的测试方法我有5个单元涵盖所有代码路径。从我到目前为止所做的研究来看,是关于异步方法被编译到的状态机,其中包含一条永远不会被采用的路径??对于这个MoveNext()问题,我还没有找到一个具体的答案。所以,即使您没有在请求委托中抛出异常,它也不会通过new JsonExceptionMiddleware((context)=>Task.CompletedTask),…
对于另一个,您只需确保抛出错误并且context.Response.HasStarted
是正确的。我很感激它——就是这样。谢谢