【发布时间】:2014-07-30 20:53:05
【问题描述】:
我在下面有这段代码,关于一个负责处理异常并记录它们的类。
using ProReserve.Reserve.Domain.Licenciados;
using ProReserve.Reserve.Domain.Sistema.Logging;
using ProReserve.Reserve.Domain.Usuarios;
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Controllers;
namespace ProReserve.Reserve.API.Filters
{
public class DefaultControllerActionInvoker : ApiControllerActionInvoker
{
private Func<HttpRequestMessage, ILoggingService> _getLoggingService;
public DefaultControllerActionInvoker(Func<HttpRequestMessage, ILoggingService> getLoggingService)
{
_getLoggingService = getLoggingService;
}
public override async Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
Task<HttpResponseMessage> actionTask = base.InvokeActionAsync(actionContext, cancellationToken); // (*)
if (actionTask.Exception != null &&
actionTask.Exception.GetBaseException() != null &&
actionContext.Request.Properties["Licenciado"] as Licenciado != null)
{
var exception = actionTask.Exception.GetBaseException();
Operacao operacao = this.getHttpStatusCode(actionTask.Result);
await SaveLogAsync(exception, actionContext.Request, operacao);
return await Task.Run(() => new HttpResponseMessage(actionTask.Result.StatusCode)
{
Content = new StringContent(exception.Message),
ReasonPhrase = "Error"
});
}
return await actionTask;
}
private async Task SaveLogAsync(Exception exception, HttpRequestMessage request, Operacao operacao)
{
var guidLog = string.Format("{0}{1}", DateTime.Now.Ticks, Thread.CurrentThread.ManagedThreadId);
var requestInfo = string.Format("{0} {1}", request.Method, request.RequestUri);
var httpContext = request.Properties["MS_HttpContext"] as HttpContextBase;
var remoteAddr = httpContext.Request.ServerVariables["REMOTE_ADDR"];
var serverName = httpContext.Request.ServerVariables["SERVER_NAME"];
var logonUser = httpContext.Request.ServerVariables["LOGON_USER"];
var usuario = request.Properties["Usuario"] as Usuario;
var message = string.Format("{0} {1}", exception.Message.ToString(), exception.InnerException != null ? exception.InnerException.ToString() : string.Empty);
var logEntry = new Log(guidLog, operacao)
{
IDUsuario = usuario.ID,
IPCliente = remoteAddr,
IPServidor = serverName,
MaquinaCliente = logonUser,
Mensagem = string.Format(@"RequestInfo: {0} - Error: {1}", requestInfo, message),
};
using (var loggingService = _getLoggingService.Invoke(request))
{
loggingService.Licenciado = (Licenciado)request.Properties["Licenciado"];
await Task.Run(() => loggingService.Inserir(logEntry));
}
}
private Operacao getHttpStatusCode(HttpResponseMessage response)
{
Operacao operacao = Operacao.Response;
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
switch (response.StatusCode)
{
case System.Net.HttpStatusCode.BadRequest: //400
operacao = Operacao.BadGateway;
break;
case System.Net.HttpStatusCode.Unauthorized: //401
operacao = Operacao.Unauthorized;
break;
case System.Net.HttpStatusCode.PaymentRequired: //402
operacao = Operacao.PaymentRequired;
break;
case System.Net.HttpStatusCode.Forbidden: //403
operacao = Operacao.Forbidden;
break;
case System.Net.HttpStatusCode.NotFound: //404
operacao = Operacao.NotFound;
break;
case System.Net.HttpStatusCode.MethodNotAllowed: //405
operacao = Operacao.MethodNotAllowed;
break;
case System.Net.HttpStatusCode.NotAcceptable: //406
operacao = Operacao.NotAcceptable;
break;
case System.Net.HttpStatusCode.ProxyAuthenticationRequired: //407
operacao = Operacao.ProxyAuthenticationRequired;
break;
case System.Net.HttpStatusCode.RequestTimeout: //408
operacao = Operacao.RequestTimeout;
break;
case System.Net.HttpStatusCode.Conflict: //409
operacao = Operacao.Conflict;
break;
case System.Net.HttpStatusCode.Gone: //410
operacao = Operacao.Gone;
break;
case System.Net.HttpStatusCode.LengthRequired: //411
operacao = Operacao.LengthRequired;
break;
case System.Net.HttpStatusCode.PreconditionFailed: //412
operacao = Operacao.PreconditionFailed;
break;
case System.Net.HttpStatusCode.RequestEntityTooLarge: //413
operacao = Operacao.RequestEntityTooLarge;
break;
case System.Net.HttpStatusCode.RequestUriTooLong: //414
operacao = Operacao.RequestUriTooLong;
break;
case System.Net.HttpStatusCode.UnsupportedMediaType: //415
operacao = Operacao.UnsupportedMediaType;
break;
case System.Net.HttpStatusCode.RequestedRangeNotSatisfiable: //416
operacao = Operacao.RequestedRangeNotSatisfiable;
break;
case System.Net.HttpStatusCode.ExpectationFailed: //417
operacao = Operacao.ExpectationFailed;
break;
case System.Net.HttpStatusCode.InternalServerError: //500
operacao = Operacao.InternalServerError;
break;
case System.Net.HttpStatusCode.NotImplemented: //501
operacao = Operacao.NotImplemented;
break;
case System.Net.HttpStatusCode.BadGateway: //502
operacao = Operacao.BadGateway;
break;
case System.Net.HttpStatusCode.ServiceUnavailable: //503
operacao = Operacao.ServiceUnavailable;
break;
case System.Net.HttpStatusCode.GatewayTimeout: //504
operacao = Operacao.GatewayTimeout;
break;
case System.Net.HttpStatusCode.HttpVersionNotSupported: //505
operacao = Operacao.HttpVersionNotSupported;
break;
default:
operacao = Operacao.Response; //51
break;
}
}
return operacao;
}
}
}
在 (*) 行 - Task<HttpResponseMessage> actionTask = base.InvokeActionAsync(actionContext, cancellationToken);) - 我有一个命令可以拦截异步操作调用。变量actionTask 返回一些属性。我需要填充其中两个属性,而不是 null。它们是:
- 结果 -> 获取返回的状态码
- 异常 -> 获取返回的异常
我需要加载这两个属性,但我不能在它们都不为空的情况下进行单元测试。在某些测试中,Result 不为 null,而在其他测试中,Exception 不为 null,但两者都不会。
下面是我的单元测试:
[TestMethod]
public void ActionAsync_Com_Exception()
{
// Arrange
Log log = null;
_loggingService.SetupSet(l => l.Licenciado = _licenciado);
_loggingService.Setup(a => a.Inserir(It.IsAny<Log>()))
.Callback<Log>((l) =>
{
log = l;
});
//Task<object> task1 = Task<object>.Factory.StartNew(() => new Exception()); //(3)
Task<object> task1 = Task<object>.Factory.StartNew(() =>
{
object myClass = new object();
return myClass;
});
//_actionDescriptor.Setup(a => a.ExecuteAsync(It.IsAny<HttpControllerContext>(), It.IsAny<IDictionary<string, object>>(), It.IsAny<CancellationToken>()))
// .Throws(new Exception("exception")); //(1)
_actionDescriptor.Setup(a => a.ExecuteAsync(It.IsAny<HttpControllerContext>(), It.IsAny<IDictionary<string, object>>(), It.IsAny<CancellationToken>()))
.Returns(task1); //(2)
DefaultControllerActionInvoker actionInvoker = new DefaultControllerActionInvoker(r => _loggingService.Object);
// Act
HttpResponseMessage response = actionInvoker.InvokeActionAsync(_baseActionContext, CancellationToken.None).Result;
// Assert
Assert.AreEqual(HttpStatusCode.InternalServerError, response.StatusCode);
Assert.AreEqual("Error", response.ReasonPhrase);
Assert.AreEqual("127.0.0.1", log.IPCliente);
Assert.AreEqual("0.0.0.0", log.IPServidor);
Assert.AreEqual("USER\\MACHINE", log.MaquinaCliente);
Assert.AreEqual("RequestInfo: POST http://localhost/teste - Error: exception ", log.Mensagem);
_loggingService.VerifySet(l => l.Licenciado = _licenciado);
_loggingService.Verify(a => a.Inserir(It.IsAny<Log>()));
_loggingService.Verify(a => a.Dispose());
}
在我的测试中我尝试过:
(1) 在这种情况下,它返回一个异常,但 Result 属性为空。
(2) 在这种情况下,它返回一个结果,但 Exception 属性为 null。在这种情况下,我还需要以某种方式进行模拟,以返回一些错误状态码(40x 或 50x)。但我不知道该怎么办。
(3) 我已经尝试创建一个触发异常的任务,但进入了上面的情况 (2)
所以,我不能让我的单元测试也正常工作。
我需要在我的单元测试代码中做些什么来加载这两个属性?
【问题讨论】:
-
支持异步测试,标记为
async Task而不是void。
标签: c# unit-testing mocking async-await moq