Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After hitting expectedexception test ends

I'm testing the delete method of an abstract base repository class which is inherited by several other repository classes. MyTestRepository inherits the base so I can run tests against the base's methods without restricting my test to using a concrete class. When I run my unit test it passes, but I noticed afterwards I have several OrderDetail and Schedule objects in the test DB that were generated by the test (objects get created during test initialization) and not deleted, while the Order object is deleted. I added some breakpoints and noticed that as soon as the helper method ends and the expected exception has been thrown the test ends and the other calls to the helper never occur.

This is my first attempt at unit testing. Are my methodologies wrong? Is ExpectedException working as intended and I'm misusing it or is there another tool I should be using? The only way I can think of to get my test is to put a try catch block in the helper and assert true when I catch my DataAccessException.

    [TestMethod]
    [ExpectedException(typeof(DataAccessException))]
    public void NHibernateRepositoryBaseDelete()
    {
        NHibernateRepositoryBaseDeleteHelper<Order, int>(myOrder, myOrder.OrderId);
        NHibernateRepositoryBaseDeleteHelper<OrderDetail, int>(myOrderDetail, myOrderDetail.OrderDetailId);
        NHibernateRepositoryBaseDeleteHelper<Schedule, int>(mySchedule, mySchedule.ScheduleId);
    }

    private static void NHibernateRepositoryBaseDeleteHelper<T, TKey>(T myItem, TKey myItemId)
    {
        MyTestRepository<T, TKey> myRepository = new MyTestRepository<T, TKey>();
        myRepository.Delete(myItem);
        myRepository.CommitChanges();

        myRepository.GetById(myItemId, false);
    }
like image 465
Justin Holbrook Avatar asked Mar 12 '09 20:03

Justin Holbrook


2 Answers

I don't generally use ExpectedException unless I can cause the exception to be thrown in a single statement - or if other tests ensure that the earlier statements don't throw the exception.

Here, you've basically got three tests - you're testing that each of those delete calls will throw an exception. All that ExpectedException does is run the method and check that it threw the exception you asked it to - it doesn't try to continue from where the exception was thrown, expecting it to be thrown again.

If you want to check that an exception is thrown by a specific piece of code (rather than just the whole method) use:

try
{
    OperationThatShouldFail();
    Assert.Fail("Expected exception");
}
catch (DataAccessException)
{
    // Expected (no need for an assertion though)
}

(And don't have ExpectedException any more - you no longer expect the test method to throw.)

You'd have one of these blocks for each of the three checks. Alternatively (and probably better) just have three tests, each of which uses ExpectedException but is only one line long. As another alternative, you could put the try/catch into your helper method.

You might also want to have an assertion at the end of the test that the relevant tables are empty - but that depends on your situation.

EDIT: As for when you clean up the database - I generally like to clean it at the start of each test so that if I run just a single failing test I can see the state of the database afterwards. If I were to clean it up in the teardown method, I'd have lost valuable information (or be forced to stay in the debugger).

EDIT: Another alternative to ExpectedException (which I suspect is in many test frameworks now) is to have a generic method like this:

static void ExpectException<T>(Action action) 
    where T : Exception
{
    try
    {
        action();
        Assert.Fail("Expected exception " + typeof(T));
    }
    catch (T)
    {
        // Expected
    }
}

You can then call it easily (multiple times) from within the method using a lambda expression for the action, assuming you're using C# 3. For example:

// Method name shortened for simplicity, and I'm assuming that type inference
// will work too.
public void NHibernateRepositoryBaseDelete()
{
    ExpectException<DataAccessException>(() => 
        DeleteHelper(myOrder, myOrder.OrderId));
    ExpectException<DataAccessException>(() => 
       DeleteHelper(myOrderDetail, myOrderDetail.OrderDetailId));
    ExpectException<DataAccessException>(() => 
       DeleteHelper(mySchedule, mySchedule.ScheduleId));
}
like image 110
Jon Skeet Avatar answered Oct 30 '22 06:10

Jon Skeet


Wrap your unit test code in a try/finally block and clean up your database inside the finally part.

[TestMethod]    
[ExpectedException(typeof(DataAccessException))]    
public void NHibernateRepositoryBaseDelete()    
{
    try
    {
        NHibernateRepositoryBaseDeleteHelper<Order, int>(myOrder, myOrder.OrderId);
        NHibernateRepositoryBaseDeleteHelper<OrderDetail, int>(myOrderDetail, myOrderDetail.OrderDetailId);
        NHibernateRepositoryBaseDeleteHelper<Schedule, int>(mySchedule, mySchedule.ScheduleId);
    }
    finally
    {
        // clean up database here
    }   
}
like image 40
Robert Lewis Avatar answered Oct 30 '22 07:10

Robert Lewis