views:

417

answers:

3

I could have sworn that I've used NUnit's Assert.Throws to determine whether or not a particular exception gets thrown from a method, but my memory has failed me before. I read this post here on SO, but it didn't answer my question, as I know the correct syntax, and I don't want to do anything with the exception that gets returned (I don't want to look at the Exception's members, though this could be useful down the road).

I wrote unit tests to prove my lack of understanding in the use of Dictionary, and couldn't get it handle the KeyNotFoundException that gets thrown. Instead of NUnit catching it and passing the test, I get an unhandled KeyNotFoundException error when I run. I verified that I don't have the VS IDE set up to break on thrown .NET exceptions.

I've tried this two ways:

Assert.Throws( typeof(KeyNotFoundException), () => value = prefs["doesn't exist"]);

and

Assert.Throws<KeyNotFoundException>( () => value = prefs["doesn't exist"]);

but both result in an unhandled exception. What am I missing here?

UPDATE seems like others can't reproduce this. Here's a screenshot:

alt text

A: 

EVEN MORE RECENTLY UPDATED ANSWER!

After our conversation in the comments added to this answer, I suspect that the nunitit test runner is the problem here. I don't believe there's anything wrong with your test as I have no problem executing it either using NUnit GUI or the excellent Resharper test runner.

UPDATED ANSWER

After seeing your screen shot, I tried stepping through my test with the debugger and saw exactly the same prompt about the unhandled exception. If I carry on stepping past that error, the test passes when I reach the end of the assertion.

When I run the test in none-debug mode using either the NUnit GUI or the Resharper 4.5 test runner, the test passes as expected every time.

Sorry to ask the obvious question, but what are you executing your test with? i.e. which test runner?

The exact code I've executed is:

using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace ClassLibrary1
{
    [TestFixture]
    public class DictionaryTest
    {


        [Test]
        public void demonstrateThatExceptionThrown()
        {
            string value;
            Dictionary<string, string> test = new Dictionary<string, string>();
            Assert.Throws(typeof(KeyNotFoundException), () => value = test["h"]);
        }
    }
}
sgreeve
I'm using the exact same version of NUnit as you are. The only difference between my test is basically that my dictionary is empty when I am retrieving the value.
Dave
thanks for updating your answer! I'm using the NUnit GUI via the NUnitit add-in.
Dave
No worries. I've installed nunitit, and (though I'm not familiar with it at all) from what I can see it insists on running the tests in debug mode, which is where I am seeing that error in your screenshot. Can you try and run the test using the NUnit GUI, in non-debug mode, and see if you still see a failure? I'm wondering whather the VS debugger isn't clever enough to know that NUnit is catching that exception in the assert.
sgreeve
dumb question, I assume you want me to run the tests with the solution set to the Release configuration? I was looking around online, and found several references to running NUnit "in debug mode", but no one said how to start it either in debug or non-debug mode.
Dave
Not exactly - I want you to run the tests without invoking Visual Studio's debugger at the same time. I suspect nunitit is kicking off the debugger (as well as building your solution in debug mode). Try this: 1. Fire up the NUnit GUI. You said we're running the same version, so it's probably on your Windows start menu as "NUnit (.NET 2.0)". 2. Once it's opened up it's window, go to File->Open project and select the DLL that the tests are in. On my box I've pointed it at the DLL in the bin/debug folder for the project, so I'm actually using a debug version of the binary as it turns out.
sgreeve
3. Click the "Run" button. It'll run your tests free of the VS debugger.See if you get a different result.
sgreeve
I might not be clear enough -- the test *passes*, but when run from nunitit, it causes an exception error message to pop up, much like when you have the "thrown" checkbox checked for .NET exceptions Debug | Exceptions. My issue with this is that the unit tests don't just run through. I have to manually hit F5 every time that exception message pops up.Perhaps this is something I should just post in the nunitit sourceforge page. Thank you for your time in looking at this.
Dave
Ah, ok. In that case, yes I agree with you - this is something that needs to be looked at by the nunitit community. I have no problem running the test with NUnit GUI or Resharper, as I've said. There's nothing at all wrong with your test. Best of luck with it.
sgreeve
thanks, I will contact them and mark this as the answer afterward!
Dave
@sgreeve: it can't be NUnitit. I just used my original approach to debugging with NUnit, where I set it as the debug application for my solution, and it does the same thing. The only thing different about my setup here is that I didn't install NUnit -- I just copied the binaries from another system. Technically, that should work. I don't know what's up with this.
Dave
@Dave - hmm, back to the drawing board. I'll try and find some time to have a play around and see if I can replicate the problem again. Not given up yet!
sgreeve
+2  A: 

Not a direct answer, but I personally prefer to tag my tests with

[ExpectedException(typeof(KeyNotFoundException))]
public Test ShouldDoTheStuff() {

  ...

}

Does this work for you? I don't actually see anything wrong with your code per se.

womp
I get the same behavior with this as well.
Dave
@womp: thanks for the answer -- I couldn't get it to work before, but after reading Pedro's answer, I saw that I had not removed the Assert.Throws<>. +1
Dave
+1  A: 

The debugger is stating that your exception is not being handled by user code, which is technically true. To demonstrate, I'll use the sample test sgreeve provided

[Test]
public void demonstrateThatExceptionThrown()
{
    string value;
    Dictionary<string, string> test = new Dictionary<string, string>();
    Assert.Throws(typeof(KeyNotFoundException), () => value = test["h"]);
}

When you execute it, you will receive a warning in VisualStudio that the exception is unhandled in user code. If you look at the callstack, you will see something like

[External Code]
CodeTests.DLL!CodeTests.MiscTests.demonstrateThatExceptionThrown.AnonymousMethod()
[External Code]
CodeTests.DLL!CodeTests.MiscTests.demonstrateThatExceptionThrown()
[External Code]

Because you have specified a delegate, the exception is happening within the "AnonymousMethod" that was created. This is being called by the .Net framework. The debugger is stopping because your delegate isn't handling the exception before it gets passed back to the framework. It doesn't care that further up the stack it might be handled in your code (perhaps since there is no way to guarantee that the external code will handle the exception correctly.)

To have VisualStudio see this as a handled exception, use the ExpectedException attribute and remove the delegate, like so:

[Test]
[ExpectedException(typeof(KeyNotFoundException))]
public void demonstrateThatExceptionThrown()
{
    string value;
    Dictionary<string, string> test = new Dictionary<string, string>();
    value = test["h"];
}
Pedro
@Pedro: thanks for the answer. I loaded that project that exhibited the NUnit "problems", but the funny thing is that all of the unit tests pass without stopping execution when the KeyNotFoundException gets thrown. Before, all unit tests would pass, but execution would stop. So I really don't know what's going on at this point. Perhaps there is something weird going on with my VS -- for example, just this week my Debug | Exception screen (CTRL-D,E) only has a "Thrown" column, whereas before it had "User-unhandled". Totally weird, but maybe related? I'll try another system.
Dave
@Pedro: awesome, I just used a different system (has the "user-unhandled" column in Debug | Exceptions), and I was able to reproduce the error there. I was also able to get your suggestion to work, so thank you! womp actually answered this earlier, but it missed some details, which your answer really cleared up for me.
Dave