首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何为以下返回json的Http调用添加异常测试

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

Stack Overflow用户
提问于 2017-03-22 11:12:43
回答 2查看 493关注 0票数 6
代码语言:javascript
运行
复制
[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%的正常运行时间和未正式定义的故障契约接口。

异常,这些异常在我的应用程序级别(业务层级别)中作为BusinessException - root类处理。

其中BusinessException的定义如下

代码语言:javascript
运行
复制
public class BusinessException : Exception
{
    BusinessException()...
    BusinessExceptionFoo()...
    BusinessExceptionBar()...
    //...
}

目前的测试方法是

To do :添加异常测试

代码语言:javascript
运行
复制
[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不测试代码的异常块。如何构造使服务抛出异常的请求,而不是手动删除以太网电缆以获取此特定类型的服务器错误。

我能想到的最好就是

代码语言:javascript
运行
复制
#if DEBUG && UnitTestExceptions
        throw new BusinessException();
#endif

但肯定有更好的选择。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-03-29 11:54:47

对于正在测试的方法,有几件事值得关注。

它正在将横切的担忧混合在行动中,这应该被重构成一个ExceptionHandler。这段代码很有可能会在控制器中重复多次,其他类似的代码也会被重复(DRY)。

代码语言:javascript
运行
复制
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中,这也假定应用程序利用了依赖反转服务。

代码语言:javascript
运行
复制
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示例

代码语言:javascript
运行
复制
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被定义为

代码语言:javascript
运行
复制
public interface IBookingService {
    BookingModel TnC(CustomViewModel viewModel, IPrincipal user);
}

使用类似于Moq的模拟框架,可以根据需要抛出异常。

代码语言:javascript
运行
复制
[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
}

为了测试如何处理异常,对异常处理程序而不是控制器进行单元测试,因为这不是控制器的责任。

尝试保持控制器的倾斜和关注它的UI关注点。

票数 2
EN

Stack Overflow用户

发布于 2017-03-29 09:18:02

正如Nkosi在他的评论中提到的,您需要做的是向任何类型的_Internal添加一个接口,这样控制器现在就依赖于接口作为契约,而不是特定的实现。

接下来,为控制器创建第二个构造函数,该构造函数接受IInternalService (无论调用什么)并将其分配给_Internal。您的非参数构造函数仍然可以分配您现在使用的任何实例。

既然有了这个配置(通常称为“穷人依赖注入”),单元测试就可以创建控制器的一个实例,传递一个抛出异常的服务的不同实现。您可以通过创建一个新类来做到这一点,也可以使用像Moq这样的库来动态地这样做。

希望这是合理的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42950014

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档