Unit testing 如何对catch块进行单元测试?

Unit testing 如何对catch块进行单元测试?,unit-testing,asp.net-mvc-4,mocking,try-catch,Unit Testing,Asp.net Mvc 4,Mocking,Try Catch,如何对try-catch块进行单元测试? 我正在使用ASP.NETMVC4.0,将Moq作为单元测试对象。我想知道如何覆盖try-catch块 下面是我的控制器操作方法的屏幕截图,包括try catch block: public ActionResult ProductDelete(int id) { try { ViewBag.Title = "Delete Product"; ProductDetailDto dto = ProductSer

如何对try-catch块进行单元测试? 我正在使用ASP.NETMVC4.0,将Moq作为单元测试对象。我想知道如何覆盖try-catch块

下面是我的控制器操作方法的屏幕截图,包括try catch block:

public ActionResult ProductDelete(int id)
{
    try
    {
        ViewBag.Title = "Delete Product";
        ProductDetailDto dto = ProductService.GetProductDetail(id);
        return View("ProductDelete", BuildProductDetailModel(dto));
    }
    catch (Exception)
    {
        throw; // cannot test this line
    }
}
您可以看到,当我从测试资源管理器启用代码覆盖着色选项时,它会显示未覆盖的catch块

下面是我的单元测试方法的屏幕截图:

[TestMethod]
public void ProductDeleteTest()
{            
    ProductController controller = new ProductController();
    var existingProductDetail = _ProductService.GetAllProductsDetails().ToList().FirstOrDefault();

    if (existingProductDetail != null)
    {
        TestControllerBuilder builder = new TestControllerBuilder();
        builder.InitializeController(controller);
        // Act
        var actual = controller.ProductDelete(existingProductDetail.ProductDetailId) as ViewResult;
        var viewModel = (ProductDetailModel)actual.Model;
        // Assert
        Assert.IsNotNull(actual);
        Assert.IsInstanceOfType(viewModel, typeof(ProductDetailModel));
        Assert.AreEqual(actual.ViewName, "ProductDelete");
    }
}

我想弄清楚如何在单元测试方法中覆盖try-catch块。

您想明确在try块中抛出异常的行。然后使用下面的方法

更新: 对不起,我在火车上,所以我急忙写下来。这是完整的答案

你的测试应该这么简单

    [TestMethod]
    public void ProductDeleteAction_GetProductDetail_ThrowsException()
    {
        // Arrange
        var productServiceMock = new Mock<IProductService>();
        productServiceMock.Setup(x => x.GetProductDetail(It.IsAny<int>())).Throws(new Exception("some"));
        var sut = new ProductController(productServiceMock.Object);

        // Act
        AssertException.Throws<Exception>(() => sut.ProductDelete(It.IsAny<int>()));
    }
有几件事需要注意:


您的测试方法名称写得很差。避免包含“Test”方法名,因为您知道它是一个单元测试。始终使用可读性很强的测试方法名称。单元测试中有一个“if”语句。避免测试中的任何逻辑,这可能会导致测试中出现错误。你的测试应该“非常”简单。因为您想要测试异常,所以您的断言是不相关的。它们可以在一个单独的单元测试中。

您希望明确在try块中抛出异常的行。然后使用下面的方法

更新: 对不起,我在火车上,所以我急忙写下来。这是完整的答案

你的测试应该这么简单

    [TestMethod]
    public void ProductDeleteAction_GetProductDetail_ThrowsException()
    {
        // Arrange
        var productServiceMock = new Mock<IProductService>();
        productServiceMock.Setup(x => x.GetProductDetail(It.IsAny<int>())).Throws(new Exception("some"));
        var sut = new ProductController(productServiceMock.Object);

        // Act
        AssertException.Throws<Exception>(() => sut.ProductDelete(It.IsAny<int>()));
    }
有几件事需要注意:


您的测试方法名称写得很差。避免包含“Test”方法名,因为您知道它是一个单元测试。始终使用可读性很强的测试方法名称。单元测试中有一个“if”语句。避免测试中的任何逻辑,这可能会导致测试中出现错误。你的测试应该“非常”简单。因为您想要测试异常,所以您的断言是不相关的。它们可以在单独的单元测试中。

您当前的测试有几个问题

  • 您并不是孤立地测试控制器(我相信您正在使用产品服务的真正实现)。您应该对正在测试的类的所有依赖项使用mock。因此,您可以避免其他类bug的影响,并且可以轻松地模拟SUT和依赖项之间的任何通信。当抛出异常时,您已经面临无法重现场景的问题
  • 测试中有条件逻辑,这使得它不可重复。记住-测试应该总是产生相同的结果
  • 您的前两个断言并没有验证任何内容,因为测试将更早失败。如果结果为
    null
    ,则在访问
    Model
    属性期间,您将出现
    NullReferenceException
    。如果模型不是
    ProductDetailModel
    类型,您将有强制转换异常
因此,您应该模拟依赖项,设置控制器和模拟之间的通信,然后采取行动。您的测试可以如下所示(此处使用库):


设置service mock以引发一些异常,然后使用
ExpectedException
属性验证控制器从服务中重新引发的异常。

您当前的测试有几个问题

  • 您并不是孤立地测试控制器(我相信您正在使用产品服务的真正实现)。您应该对正在测试的类的所有依赖项使用mock。因此,您可以避免其他类bug的影响,并且可以轻松地模拟SUT和依赖项之间的任何通信。当抛出异常时,您已经面临无法重现场景的问题
  • 测试中有条件逻辑,这使得它不可重复。记住-测试应该总是产生相同的结果
  • 您的前两个断言并没有验证任何内容,因为测试将更早失败。如果结果为
    null
    ,则在访问
    Model
    属性期间,您将出现
    NullReferenceException
    。如果模型不是
    ProductDetailModel
    类型,您将有强制转换异常
因此,您应该模拟依赖项,设置控制器和模拟之间的通信,然后采取行动。您的测试可以如下所示(此处使用库):


设置service mock以引发一些异常,然后使用
ExpectedException
属性验证控制器从服务中重新引发的异常。

请下次粘贴代码而不是粘贴图像-这有助于其他人复制粘贴代码,当图像从主机中删除时,您的问题不会过时。当然,lazyberezovsky我会处理。谢谢您的更改。请下次粘贴代码而不是粘贴图像-这有助于其他人复制粘贴代码,当图像从主机上删除时,您的问题不会过时。当然,lazyberezovsky我会处理好的。谢谢您的更改。谢谢您的建议对我非常有用。我是单元测试新手,所以您能给我推荐一些好文章或其他东西来学习它吗?@Pawan我能推荐的最好的东西是这本书通过示例演示了如何通过TDD方法创建应用程序。您还可以阅读关于编写有效单元测试的文章。强烈建议阅读Bob叔叔关于编写好的可测试代码的原则谢谢你的建议对我真的很有帮助。我是单元测试新手,你能给我推荐一些好文章或其他东西来学习它吗?@Pawan我能推荐的最好的东西是一本书,它通过示例演示了如何通过TDD方法创建应用程序。您还可以阅读关于编写有效单元测试的文章。强烈建议阅读Bob叔叔关于编写好的可测试代码的原则
    public ProductController(IProductService productService) {
        _productService = productService;
    }

    public ActionResult ProductDelete(int id)
    {
        try
        {
            ViewBag.Title = "Delete Product";
            ProductDetailDto dto = _productService.GetProductDetail(id);
            return View("ProductDelete", BuildProductDetailModel(dto));

        }
        catch (Exception)
        {
            throw;
        }
    }
private ProductController controller;
private Mock<IProductService> productServiceMock;
private Random random = new Random();

[TestInitialize]
public void Init()
{
    productServiceMock = new Mock<IProductService>();
    controller = new ProductController(productServiceMock.Object);
}

[TestMethod]
public void ShouldReturnDetailsOfDeletedProduct()
{
    int id = random.Next();
    var dto = new ProductDetailDto { ProductDetailId = id };
    productServiceMock.Setup(s => s.GetProductDetail(id)).Returns(dto);

    // Act
    var actual = controller.ProductDelete(id) as ViewResult;
    var viewModel = (ProductDetailModel)actual.Model;
    // Assert
    Assert.AreEqual("ProductDelete", actual.ViewName);
    Assert.AreEqual(id, viewModel.Id);
    productServiceMock.VerifyAll();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldNotReturnResultWhenExceptionOccurs()
{
    int id = random.Next();
    var dto = new ProductDetailDto { ProductDetailId = id };
    productServiceMock.Setup(s => s.GetProductDetail(id))
                      .Throws(new ArgumentException());
    // Act
    controller.ProductDelete(id);
}