【问题标题】:Right way of testing for Exceptions测试异常的正确方法
【发布时间】:2013-10-12 08:37:34
【问题描述】:

我在一个包含 80% 简单逻辑和 20% 复杂逻辑的项目中进行 TDD。如果某些方法抛出错误并想知道正确的方法,我会发现自己进行了很多测试。我使用 NUnit 和 JustMock。

我有两种方法可以做到这一点。使用 ExpectedException 属性,并指定类型。或者写如下。编写如下的专业人士是,我还可以断言 exception.message(如果我制作了自定义的),如果测试失败,我也会显示 exception.message。但我想和其他人一起检查你是如何做到的。所以总结一下:

  1. 对此类异常进行大量测试是否正常?
  2. 这是正确的做法吗:

只是解释一下:供应商提供某些合同,部门接受一份合同但不能与同一供应商签订多个合同(但当然可以与不同供应商签订不同的合同)

    [Test]
    public void Accepting_more_than_one_contract_from_supplier_throws_exception()
    {
        //Arrange
        var department = new Department(Guid.NewGuid(), "1234");
        var supplier = Mock.Create<Supplier>();
        var contract1 = Mock.Create<DeliveryContract>();
        var contract2 = Mock.Create<DeliveryContract>();
        var id = Guid.NewGuid();
        supplier.Arrange(x => x.Id).Returns(id);
        contract1.Arrange(x => x.Supplier).Returns(supplier);
        contract2.Arrange(x => x.Supplier).Returns(supplier);

        //Act
        department.AcceptContract(contract1);

        //Assert
        try
        {
            department.AcceptContract(contract2);
            Assert.Fail("Duplicate contract with supplier did not throw an exception");
        }
        catch (Exception ex)
        {   
            Assert.AreEqual(typeof(ArgumentException),ex.GetType(),ex.Message);
        }
    }

【问题讨论】:

    标签: c# unit-testing tdd nunit domain-driven-design


    【解决方案1】:

    你可以使用Assert.Throws方法:

    Assert.Throws<ArgumentException>(() => department.AcceptContract(contract2));
    

    Assert.Throws<ArgumentException>(() => department.AcceptContract(contract2), "some message");
    

    【讨论】:

    • 这是否比上述方法更好?
    • 是的,它是在 NUnit 中定义的。你不觉得它比 try-catch 块更好吗?
    • 是的,我确实这么认为,但 ExceptionAttribute 也是如此;)但我确实认为 try/catch 块确实显示了测试的很多内涵,以及它应该如何工作。此外还有断言异常消息的能力,这更加独特。在断言错误的参数异常时遇到了一些问题,即如果我在另一个字段中检查空字符串并引发 ArgumentException 并且测试通过了错误的术语。
    • 是的,ExceptionedException 是另一种选择。我猜这是一个选择问题。
    【解决方案2】:

    正如@Ufuk 所说。使用Assert.Throws&lt;T&gt;,其中 T 是异常类型。

    如果您还想检查异常消息,请使用以下之一:

    T Assert.Throws<T>( TestDelegate code, string message );
    T Assert.Throws<T>( TestDelegate code, string message, 
            params object[] parms);
    

    顺便说一下,您为测试创建的数据不应该是模拟数据,而是存根数据。 存根是指仅包含测试数据的对象,在您的情况下,您为对象提供行为 - 而不是数据。

    我建议您使用 NBuilder code.google.com/p/nbuilder/。您可以通过它创建清晰的存根。它还具有创建对象的极大灵活性,它们可以随机创建或完全为您的测试用例准备。

    在您的情况下的用法:

    var supplierStub = Builder
                    .With(供应商 => 供应商.Id = id)
                    。建造();
    
    var contractStub = Builder.CreateListOfSize(2)
                    。全部()
                        .With(合同=>合同。供应商=供应商存根)
                    。建造();
    

    【讨论】:

    • 感谢您对 NBuilder 的建议。只有一个问题,您是否发现自己做了很多此类测试,或者测试此类异常的代码很糟糕?
    • 取决于类逻辑流程。在您的示例中,抛出异常并不明显,它是类逻辑的一部分,因此对其进行测试是合理的。请记住,测试可能是您代码的良好文档。我主要在代码中直接抛出异常(通过抛出)或程序在某些情况下可能抛出异常时测试异常代码。对我来说,一个很好的例子是测试负责读取 .xml 文件 f.e 的 servis。通过 LinqToXml。一个很好的测试案例是如果缺少一个节点,服务将如何表现。
    猜你喜欢
    • 2017-07-11
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-05
    • 2021-10-28
    • 2018-12-31
    • 2017-06-06
    相关资源
    最近更新 更多