c# - 如何为以下返回 json 的 Http 调用添加异常测试

标签 c# asp.net-mvc unit-testing mstest

[HttpPost]
[Route("TnC")]
public IHttpActionResult TnC(CustomViewModel myViewModel)
{
    try
    {
        return Json(_Internal.TnC(myViewModel, LoggedInUser));
    }
    catch (BusinessException exception)
    {
        return Json(BuildErrorModelBase(exception));
    }
}

其中 _Internal 是一个保证 99.99% 正常运行时间的服务,并且没有定义形式化的故障契约接口(interface)。

在我的应用程序级别(业务层级别)作为 BusinessException 处理的异常 - 根类

其中BusinessException定义如下

public class BusinessException : Exception
{
    BusinessException()...
    BusinessExceptionFoo()...
    BusinessExceptionBar()...
    //...
}

而目前的测试方法是

要做的事:添加异常测试

[TestMethod]
[ExpectedException(typeof(BusinessException),
        "Not a valid Business Case")]
public void TnCTest()
{
    var bookingService = myContainer.Resolve<mySvc>();

    var controller = new LinkBookingController(mySvc, myContainer);
    var expectedResult = controller.TnC(new myViewModel
    {
        ...params
    });

    var actualResult = GetData<Result<myViewModel>>(expectedResult);

    Assert.AreEqual(expectedResult, actualResult);
}

expectedResult==actualResult 不测试代码的异常 block 。 除了手动移除以太网电缆以获得这种特定类型的服务器错误之外,我如何构造一个使服务抛出异常的请求。

我能想到的最好的是

#if DEBUG && UnitTestExceptions
        throw new BusinessException();
#endif

但肯定有更好的选择。

最佳答案

被测方法有一些值得关注的地方。

它在应该重构为 ExceptionHandler 的操作中混合了横切关注点。很可能这段代码在那个 Controller 和其他类似的 Controller 中重复了很多次 (DRY)。

public class WebApiExceptionHandler : ExceptionHandler {

    public override void Handle(ExceptionHandlerContext context) {
        var innerException = context.ExceptionContext.Exception;
        // Ignore HTTP errors
        if (innerException.GetType().IsAssignableFrom(typeof(System.Web.HttpException))) {
            return;
        }

        if(innerException is BusinessException) {
            context.Result = BuildErrorResult(exception);
            return;
        }

        //...other handler code
    }

    IHttpActionResult BuildErrorResult(BusinessException exception) { 
        //... your logic here 
    }
}

以下扩展方法可用于在启动期间将处理程序添加到 HttpConfiguration,这也假定应用程序正在利用依赖倒置服务。

public static HttpConfiguration ReplaceExceptionHandler(this HttpConfiguration config) {
    var errorHandler = config.Services.GetExceptionHandler();
    if (!(errorHandler is WebApiExceptionHandler)) {
        var service = config.Services.GetService(typeof(WebApiExceptionHandler));
        config.Services.Replace(typeof(IExceptionHandler), service);
    }
    return config;
}

既然已经处理了横切关注点,那么操作就变得简单多了,也更容易测试。这是 ApiController 的简化示例

public class LinkBookingController : ApiController {
    private IBookingService bookingService;

    public LinkBookingController(IBookingService service) {
        bookingService = service;
    }

    [HttpPost]
    [Route("TnC")]
    public IHttpActionResult TnC(CustomViewModel myViewModel) {

        return Json(bookingService.TnC(myViewModel, User));

    }
}

其中 IBookingService 定义为

public interface IBookingService {
    BookingModel TnC(CustomViewModel viewModel, IPrincipal user);
}

使用像 Moq 这样的模拟框架,可以根据需要抛出异常。

[TestMethod]
[ExpectedException(typeof(BusinessException), "Not a valid Business Case")]
public void TnC_Should_Throw_BusinessException() {
    //Arrange
    var bookingService = new Mock<IBookingService>();

    var controller = new LinkBookingController(bookingService.Object);

    var viewModel  = new myViewModel
    {
        //...params
    };

    bookingService.Setup(_ => _.TnC(viewModel, It.IsAny<IPrincipal>())).Throws<BusinessException>()

    //Act
    var expectedResult = controller.TnC(viewModel);

    //Assert
    //...the ExpectedException attribute should assert if it was thrown
}

要测试如何处理该异常,请对异常处理程序而不是 Controller 进行单元测试,因为这不是 Controller 的责任。

尽量保持 Controller 精简并专注于其 UI 问题。

关于c# - 如何为以下返回 json 的 Http 调用添加异常测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42950014/

相关文章:

c# - .NET 与 MySqlConnection 和连接池

c# - 我应该为我使用的每个数据库设置一个类吗?

抛出 Angular 6 更新规范 [object ErrorEvent]

c++ - OpenGL 如何进行单元测试?

C# 智能感知库

c# - 没有管理员权限无法启动 nancy self host

asp.net-mvc - 在 Controller 上找不到公共(public)操作方法 pagerror.gif/refresh.gif - 谁在调用该 gif?

jquery - 制作下拉列表多面板

c# - 强制 Controller 操作将 "Type"作为图像而不是文档返回

java - ArchUnit 类应该仅依赖于包中的特定类