Unit testing 抛出特殊类型的异常以终止单元测试

Unit testing 抛出特殊类型的异常以终止单元测试,unit-testing,Unit Testing,假设我想编写一个单元测试来测试在方法中实现的特定功能。如果我想完全执行该方法,我必须做一些额外的设置工作(模拟对象等)。我没有这样做,而是使用以下方法: -我设置了我对验证感兴趣的期望,然后使测试方法抛出一种特殊类型的异常(例如TerminateTestException)。 -在进一步的单元测试中,我捕获异常并验证模拟对象的期望 这很好,但我不确定这是否是一个好的做法。我不经常这样做,只是在节省时间和精力的情况下。作为反对使用此方法的论点,我想到的一件事是抛出异常需要很长时间,因此测试执行速度

假设我想编写一个单元测试来测试在方法中实现的特定功能。如果我想完全执行该方法,我必须做一些额外的设置工作(模拟对象等)。我没有这样做,而是使用以下方法:
-我设置了我对验证感兴趣的期望,然后使测试方法抛出一种特殊类型的异常(例如TerminateTestException)。
-在进一步的单元测试中,我捕获异常并验证模拟对象的期望

这很好,但我不确定这是否是一个好的做法。我不经常这样做,只是在节省时间和精力的情况下。作为反对使用此方法的论点,我想到的一件事是抛出异常需要很长时间,因此测试执行速度比使用其他方法慢

编辑:
只是澄清一下,我不是在修改SUT代码。我要做的是提供一个模拟对象或重写SUT类,以便在执行我感兴趣的部分后,SUT退出执行

    private class TestCalculationService : CalculationService
    {
        public bool ValidateForSyncCalled;

        protected override void ValidateForSyncCall()
        {
            ValidateForSyncCalled = true;
            throw new ExceptionToTerminateTest();
        }                
    }  


    [TestMethod]
    public void CalculationService_Calculate_Sync_Calls_ValidateForSyncCall()
    {
        InitializeMocks();
        TestCalculationService calculationService = new TestCalculationService();
        calculationService.MessageInstanceFactory = _mockMessageInstanceFactory;

        try
        {
            calculationService.Calculate( null);
            Assert.Fail("Exception should have been thrown");
        }
        catch (ExceptionToTerminateTest)
        {
            //ok
        }

        Assert.IsTrue(calculationService.ValidateForSyncCalled );
    }

如果您能够成功地单独测试一个方法的某些部分,那么该部分方法本身就应该是一个较小方法的候选者。我更喜欢重构而不是较小的方法,而不是在执行过程中检测模拟对象以中止方法,或者更糟糕的是,修改实际代码以了解测试。

我不担心对速度的影响-这将是最小的

但我认为这不是一个好的做法。主要是因为,如果您专门为了方便测试而添加客户机代码(而不是将类设计为可测试的),这总是一个坏迹象。您正在测试的方法是否可能做得太多?如果您有一个很好的理由将该方法留在测试中,那么可能该代码块本身就是一个方法

我还想知道如何在运行时避免异常。如果您开始使用这样的代码:

if (IsTesting)
{
    throw new TerminateTestException();
}
然后,可以提出这样的论点:您正在测试的代码与实际运行的代码不同。在这种情况下,你真的在测试什么吗


最后一个想法是:我假设您正在为至少一个测试用例(一直运行的用例,或者确保在不应该抛出异常时不会抛出异常的测试用例)设置所有模拟。如果您这样做,那么可能您在测试类本身中缺少了一些重构的可能性。您不需要为每个测试单独建立模拟对象

在我看来,您最好将这段代码提取到它自己的方法中并进行测试。它被称为萌芽方法