5

I'm working on unit-tests for an application which has a constructor that takes three values as arguments. The numbers shall be 0 or higher, and now I'm writing on an unit-test for the constructor that throws an exception if this is not the case.

What I can't figure out is how I what to write after "Assert" to determine this so that the test passes if illegal numbers are passed to the constructor. Thanks in advance.

EDIT: I'm using MSTest framework

   public void uniqueSidesTest2()
    {
        try {
            Triangle_Accessor target = new Triangle_Accessor(0, 10, 10);
        }
        catch (){
            Assert // true (pass the test)
            return;
        }

        Assert. // false (test fails)
    }

// From the code...

    public Triangle(double a, double b, double c) {
        if ((a <= 0) || (b <= 0) || (c <= 0)){
            throw new ArgumentException("The numbers must higher than 0.");
        }
        sides = new double[] { a, b, c };
    }
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
holyredbeard
  • 19,619
  • 32
  • 105
  • 171
  • I'm not sure in bog standard MS test, but in NUnit you would annotate the test to expect an exception. You still get an error but it doesn't count as a failure e.g. ExpectedException( typeof( ArgumentException ) )] – dougajmcdonald Jan 05 '12 at 15:20
  • duplicate of http://stackoverflow.com/questions/933613/c-how-do-i-use-assert-unit-testing-to-verify-that-an-exception-has-been-thro – ken2k Jan 05 '12 at 15:21

6 Answers6

10

First of all, you should throw an ArgumentOutOfRangeException rather than just an ArgumentException.

Second, your unit test should expect an Exception to be thrown, like so:

[ExpectedException(typeof(ArgumentOutOfRangeException))]
public static void MyUnitTestForArgumentA()
{
    ...
}

So, you need to create separate unit tests -- one for each argument -- that test whether the method throws a correct exception when the argument is out of range.

Roy Dictus
  • 32,551
  • 8
  • 60
  • 76
  • Thanks! I've changed to ArgumentOutOfRangeException in the code, but when I added [ExpectedException(typeof(ArgumentOutOfRangeException))] i got the following error message: "Error 3 The type or namespace name 'ArgumentOutOfRangeException' could not be found (are you missing a using directive or an assembly reference?)"? – holyredbeard Jan 05 '12 at 15:47
  • It's in the System namespace, so if you can find ArgumentException you should automatically also find ArgumentOutOfRangeException. Are you sure you spelled it 100% right? – Roy Dictus Jan 05 '12 at 15:50
  • See also http://msdn.microsoft.com/en-us/library/system.argumentoutofrangeexception.aspx. – Roy Dictus Jan 05 '12 at 15:50
  • Do you happen to be missing a `using System;` in the class file? That exception is in mscorlib.dll and the System namespace. – Anthony Pegram Jan 05 '12 at 15:51
4

No need to use a try catch block. Using NUnit or the MSTest framework you can use an attribute on your test method declaration to specify that you expect an exception.

MSTest

 [TestMethod]
 [ExpectedException(typeof(ArgumentException))]
 public void uniqueSidesTest2()
Myles McDonnell
  • 12,943
  • 17
  • 66
  • 116
  • I would advice against using that attribute because you don't know which method is responsible for throwing the exception. – Toni Parviainen Jan 05 '12 at 15:40
  • @Toni, care to expand, because I am not following your line of thought. The only issue I might have is the exception might not be specific enough, the method name isn't very good, but both of those are traits inherited from the question directly. But the methodology is sound. – Anthony Pegram Jan 05 '12 at 15:45
  • @AnthonyPegram If you use the ExpectedException you don't know which line in your test threw that exception. [Simple example](http://pastebin.com/FBRPac2n). In that example you don't know which method threw the exception so the test might pass even though wrong thing threw exception. – Toni Parviainen Jan 05 '12 at 17:21
  • @Toni, that's not an effective unit test, it's doing too much, *testing more than one thing*. And it's also not indicative of the situation here, where the question is very clearly only testing the constructor. – Anthony Pegram Jan 05 '12 at 17:39
  • @AnthonyPegram My example was bad. The point I'm trying to make is that if you use ExpectedException attribute you don't know if the exception is thrown from the right method. You might have some setup or you are testing some specific usage scenario. Not every unit test test only single method. You are right, in this particular case (testing only constructor) ExpectedException usage works. Too many times people who learn about it abuse it and use it every time and sometimes even with integration tests. – Toni Parviainen Jan 06 '12 at 09:18
2

It may not be the best solution, but if I'm testing to make sure an Exception is thrown, I will do something like the following:

public void uniqueSidesTest2()
{
    try {
        Triangle_Accessor target = new Triangle_Accessor(0, 10, 10);
        Assert.Fail("An exception was not thrown for an invalid argument.");
    }
    catch (ArgumentException ex){
        //Do nothing, test passes if Assert.Fail() was not called
    }
}

Since your constructor call should throw an error, if it ever gets to the second line (The Assert.Fail() line) then you know it didn't properly throw the exception.

jamesmillerio
  • 3,154
  • 4
  • 27
  • 32
  • 1
    You should _at least_ catch the appropriate exception otherwise you'll succeed if _any_ exception is thrown and that's not entirely correct. – Austin Salonen Jan 05 '12 at 15:28
  • Very true, I was just trying to give a basic explanation of how I've done it in the past and just copy/pasted his code. I'll make some edits. Thanks for the heads up. – jamesmillerio Jan 05 '12 at 15:37
2

If you don't have nunit (or other framework that has this support built in you can use the following type of helper method

 public static void ThrowsExceptionOfType<T>(Action action) where T: Exception
    {
        try
        {
            action();
        }
        catch (T)
        {
            return;
        }
        catch (Exception exp)
        {
            throw new Exception(string.Format("Assert failed. Expecting exception of type {0} but got {1}.", typeof(T).Name, exp.GetType().Name));
        }

        throw new Exception(string.Format("Assert failed. Expecting exception of type {0} but no exception was thrown.", typeof(T).Name));
    }

Your test would look like this

AssertHelper.ThrowsExceptionOfType<ArgumentException>( 
    () => 
    {
        new Triangle_Accessor(0, 10, 10);
    });
Toni Parviainen
  • 2,217
  • 1
  • 16
  • 15
  • This is what I do, too. I also use an overload of the helper method that has an output parameter that gets set to the thrown exception. This lets you inspect the exception, if needed. – joelsand Feb 05 '13 at 20:15
  • public static void ThrowsException(out TException thrownException, Action action) where TException : Exception { try { action.Invoke(); // if no exception was thrown, fail Assert.Fail("Assert.ThrowsException failed - Expected exception not thrown: " + typeof(TException).ToString()); thrownException = null; } catch (TException ex) { thrownException = ex; } } – joelsand Feb 05 '13 at 20:16
0

You will not need an Assert in the catch (but you might want to catch a more specific exception, like ArgumentException).

To always fail, there is an Assert.Fail.

Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
0

You don't mention what framework you are using for unit testing, but I think what you're looking for is something like what is shown here:

http://www.nunit.org/index.php?p=exception&r=2.4

tnktnk
  • 512
  • 2
  • 7
  • 20