假设我想编写单元测试来测试在方法中实现的特定功能。如果我想完全执行该方法,我将不得不做一些额外的设置工作(模拟对象期望等)。而不是这样做我使用以下方法:
- 我设置了我对验证感兴趣的期望,然后使测试的方法抛出一种特殊类型的异常(例如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 );
}
答案 0 :(得分:3)
如果您可以单独成功地测试方法的某些部分,那么该方法的该部分应该是较小方法的候选者。我更倾向于使用较小的方法来修复模拟对象,以便在执行过程中中断方法,或者更糟糕的是,修改实际代码以了解测试。
答案 1 :(得分:0)
我不担心对速度的影响 - 它会很小。
我不认为这是好习惯。主要是因为如果你专门为了便于测试而添加客户端代码(而不是设计你的类是可测试的),这总是一个坏兆头。你测试的方法可能做得太多了吗?如果您有充分的理由将该方法保留在测试中,那么该代码块本身应该是一种方法吗?
我也想知道如何在运行时避免异常。如果您开始使用这样的代码:
if (IsTesting)
{
throw new TerminateTestException();
}
然后可以证明您正在测试的代码与实际运行的代码不同。在那种情况下,你真的在测试什么吗?
最后一个想法:我假设您正在为至少一个测试用例设置所有模拟(一直运行的情况,或测试用例以确保异常不是当它不应该被抛出时)。如果你这样做,也许你错过了测试类本身的一些重构可能性。您不应该单独为每个测试建立模拟对象。
答案 2 :(得分:0)
在我看来,你最好将这段代码提取到自己的方法中并进行测试。它被称为 Sprout Method 。