1

Before I start, I want to make it clear that I've already checked for solutions in both this question and this question.

The Method to Test

public static DataSet ExecuteDataSet(this SqlConnection connection, string sql)
{
    if (null == connection || null == sql)
    {
        throw new ArgumentNullException();
    }

    using (var command = connection.CreateCommand())
    {
        // Code elided for brevity
    }
}

The Test Methods

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void ExecuteDataSetThrowsForNullConnection()
{
    ((SqlConnection)null).ExecuteDataSet("SELECT TOP 1 * FROM prep");
}

[Test]
public void ExecuteDataSetThrowsForNullSql()
{
    Assert.Throws<ArgumentNullException>(
        () => Resource.Connection.ExecuteDataSet(null)
    );
}

The Odd Behavior

Neither version of the test method is catching the ArgumentNullException that is thrown immediately upon entering the ExecuteDataSet method. Control flow proceeds to the next line (using (var command = connection.CreateCommand())) and a NullReferenceException is occurring instead (which, of course, isn't handled by either of my test cases because it should never be thrown).

Originally, the first test method (ExecuteDataSetThrowsForNullConnection) looked just like the second one (ExecuteDataSetThrowsForNullSql). When Assert.Throws failed to catch the exception, I did some research and noted that some folks recommended using ExpectedException instead. I modified the test code accordingly, but to no avail.

For the record, this is 32-bit .NET 3.5 code, tested under NUnit 2.5.9. I'm using TestDriven.NET for Visual Studio integration, and have the latest versions of NCover and NDepend installed.

TL;DR Question

Why aren't the test methods catching the exception that is thrown, and how do I fix it?

EDIT

This version of the test method works.

[Test]
public void ExecuteDataSetThrowsForNullConnection()
{
    try
    {
        ((SqlConnection)null).ExecuteDataSet("SELECT TOP 1 * FROM prep");
    }
    catch(ArgumentNullException e)
    {
        Assert.AreEqual(true, true);
    }
    catch (Exception e)
    {
        Assert.Fail("Expected ArgumentNullException, but {1} was thrown instead.", e.GetType().Name);
    }
}
Community
  • 1
  • 1
Mike Hofer
  • 16,477
  • 11
  • 74
  • 110
  • If you go back to the original test method, does that still fail? If you have all three in the same test class, does only the last one work? What if you put an Assert.Fail after the ExecuteDataSet call, to ensure that an exception *is* actually thrown? – Jon Skeet Jan 21 '11 at 17:39

1 Answers1

3

My guess is that you're not really testing the code you think you are. Try putting some Console.WriteLine statements in and see if they're printed. If you put a breakpoint on the throw statement and run the tests in the debugger, does the breakpoint get hit? If control is passing to the next statement, that means the exception is never being thrown - it can't possibly be caught in a way which would let execution continue in the throwing method, unless you've found a really weird CLR bug.

I've written lots of code like this and it's never failed in NUnit.

As an aside, I view it as good practice to include the parameter name in ArgumentExceptions, so I'd have written:

if (connection == null)
{
    throw new ArgumentNullException("connection");
}
if (sql == null)
{
    throw new ArgumentNullException("sql");
}

This has the unfortunate problem of being longwinded and repeating the parameter names in the code as strings... but it's reasonably easy to get right (especially with ReSharper helping to validate the names) and it could be really useful if this ever triggers in production. (There are some grotty hacks to validate the arguments in other ways, but I suspect you don't want to see those...)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks, Jon. It turns out that the exception was being thrown on a completely different line of code, and the IDE was reporting the exception on the `using` statement. Once I fixed the *actual* NullReferenceException, I was able to get the standard means of testing for them working again. I'm still kind of stumped as to why `Assert.Throws` didn't catch an exception that should have bubbled up the call stack, tho. It's likely I haven't looked at it the right way yet. – Mike Hofer Jan 21 '11 at 17:42
  • Oh, and oddly enough, I *did* step through the code, and I was, in fact, throwing `ArgumentNullException`. I have no idea why control was then transferred to the `using` statement. – Mike Hofer Jan 21 '11 at 17:43
  • 2
    @Mike: If you're still able to go back to the "odd" way, it may be worth trying to reproduce that and maybe ask another question about Assert.Throws, but with a short but complete program to reproduce what was actually going on. Glad this one's sorted though :) As I say, if you're throwing an exception and it's still stepping over to the next line, that sounds like you've got a disconnect between the debugger and the source code. Modifying the code to print out what's going on often helps in that situation :) – Jon Skeet Jan 21 '11 at 17:44