tags:

views:

37

answers:

3

Assume I want to write a unit test to test a particular piece of functionality that is implemented within a method. If I wanted to execute the method completely, I would have to do some extra set up work (mock objects expectations etc.). Instead of doing that I use the following approach:
- I set up the expectations I'm interested in verifying and then make the tested method throw a special type of exception (e.g. TerminateTestException).
- Further down in the unit test I catch the exception and verify the mock object expectations.

It works fine but I'm not sure it is good practice. I do not do this regularly, only in cases where it saves me time and effort. One thing that comes to mind as an argument against using this is that throwing exceptions takes long time so the tests execute slower than if I used a different approach.

EDIT:
Just to clarify, I'm not modifying the SUT code. What I do I provide a mock object or override the SUT class so that the SUT quits execution after the part I'm interested in is executed.

    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  A: 

If you can successfully test portions of a method in isolation, then that portion of the method should be a candidate for a smaller method on its own. I'd prefer refactoring to the smaller method over either instrumenting mock objects to abort a method part way through execution or, even worse, modifying the actual code to be aware of the test.

tvanfosson
A: 

I wouldn't worry about the effect on speed - it'll be minimal.

I don't think it is good practice though. Mainly because it is always a bad sign if you are adding client code specifically to facilitate testing (as opposed to designing your classes to be testable). Is it possible that the method you are testing is doing too much? If you have a good point to leave the method in the test, maybe that block of code should be a method by itself?

I'm also wondering about how you avoid the exception during runtime. If you start having code like this:

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

Then the argument could be made that the code you are testing is different to the code that actually gets run. In that case, are you really testing anything?

And one, final, thought: I assume that you are setting up all the mocks for at least one test case (the case that runs all the way through, or the test case to ensure the exception isn't thrown when it shouldn't be). If you are doing that, maybe you are missing some refactoring possibilities in the test class itself. You shouldn't need to establish your mock objects for every test individually.

Martin Harris
A: 

it seems to me that you would be better of extracting that piece of code into its own method and test that. It is called Sprout Method.

Gutzofter