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

时间:2010-05-21 16:24:53

标签: unit-testing

假设我想编写单元测试来测试在方法中实现的特定功能。如果我想完全执行该方法,我将不得不做一些额外的设置工作(模拟对象期望等)。而不是这样做我使用以下方法:
- 我设置了我对验证感兴趣的期望,然后使测试的方法抛出一种特殊类型的异常(例如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 );
    }

3 个答案:

答案 0 :(得分:3)

如果您可以单独成功地测试方法的某些部分,那么该方法的该部分应该是较小方法的候选者。我更倾向于使用较小的方法来修复模拟对象,以便在执行过程中中断方法,或者更糟糕的是,修改实际代码以了解测试。

答案 1 :(得分:0)

我不担心对速度的影响 - 它会很小。

我不认为这是好习惯。主要是因为如果你专门为了便于测试而添加客户端代码(而不是设计你的类是可测试的),这总是一个坏兆头。你测试的方法可能做得太多了吗?如果您有充分的理由将该方法保留在测试中,那么该代码块本身应该是一种方法吗?

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

if (IsTesting)
{
    throw new TerminateTestException();
}

然后可以证明您正在测试的代码与实际运行的代码不同。在那种情况下,你真的在​​测试什么吗?

最后一个想法:我假设您正在为至少一个测试用例设置所有模拟(一直运行的情况,或测试用例以确保异常不是当它不应该被抛出时)。如果你这样做,也许你错过了测试类本身的一些重构可能性。您不应该单独为每个测试建立模拟对象。

答案 2 :(得分:0)

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