How can I verify an exception was thrown with a certain parameter? - c#

Suppose I have the following exception and method:
public MyException(string type)
{
/* Does soemthing with the parameter */
}
public void DoSomething()
{
// ...
if (/* some rule */)
throw new MyException("A");
else
{
throw new MyException("B");
}
}
I want to test my method, how can I verify using Visual Studio Unit Testing Framework that MyException with a specific parameter was thrown? I'd usually go with [ExpectedException(typeof(MyException)] but I wouldn't know which of these exceptions.

I'd usually go with [ExpectedException(typeof(MyException)]
I suggest you don't do that. You haven't told us which unit test framework you're using, but these days most provide something like:
Assert.Throws<MyException>(() => testSubject.DoSomething());
Aside from anything else, that will make sure the exception is only thrown where you expect it to be - not elsewhere.
Next, depending on your framework, you may find that Assert.Throws returns the exception thrown, so you can then validate things in that:
var exception = Assert.Throws<MyException>(...);
Assert.AreEqual("A", exception.Message);
Note that testing the exact message is generally pretty brittle - but if it's testing something else about the exception (e.g. ArgumentException.ParamName) that's more clear-cut.

Related

How do you test for the specific reason an exception was thrown?

What is the correct way to determine exactly what caused an exception, to correct it?
Consider the code below. I attempt to parse a string of XML, but occasionally, the incoming XML will not be top-level, meaning it needs to be surrounded by a root element.
Which this happens, the parser throws an XmlException, but it could throw that for a lot of reasons. I want to catch this one specific reason.
I do this, which I concede is probably not great:
var doc = new XmlDocument();
try
{
doc.LoadXml(xml);
}
catch(XmlException e)
{
if(e.Message.Contains("multiple root elements"))
{
doc.LoadXml($"<root>{xml}</root>");
}
else
{
throw e;
}
}
This feels like a hack. What is the correct way to do this?
There is a new feature of C# that allows you to filter exceptions in the catch clause with when keyword:
try
{
}
catch (XmlException ex) when ( ex.Message.Contains(...) )
{
//handle
}
You can use multiple fields to recognize the exception type, like the InnerException, StackTrace, and Data. As #David Hruška suggests, the HResult property is also a good place to check to recognize the type of the exception
The Message is not the best property to use for the check, as it is usually localized for the built-in types and as a result might look different with other culture setting.
you can try to make a switch for XmlException.HResult as described here:
https://msdn.microsoft.com/en-us/library/system.xml.xmlexception(v=vs.110).aspx
The only thing I am not sure, if it points the the specific exception type (like XmlException) or specific exception "message".
If this does not help, I think you have no other option than checking for message.
EDIT: Also, as was pointed above, you should throw; instead of throw e; as the second clears the StackTrace. ReSharper also warns about this one.
There are multiple ways to achieve what you're trying to do.
Different test frameworks bring their own tools to help with this too.
For example, if you're using MSVS Test Framework, the simplest option is to only check for the exception type. In this case you just mark the test method with "ExceptedExceptionAttribute" and specify the expected exception type to be thrown from the method, as follows:
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Action_ThrowsException()
{
// test logic here
}
There is another constructor available, which allows to specify the exact exception message as the second parameter, which is rarely used. You can find the documentation for ExpectedExceptionAttribute in MSDN.
Another option, where you have more control, would be what was already suggested in the other answers, which can be encapsulated in a helper method as follow:
private static T Throws<T>(Action action) where T : Exception
{
try
{
action();
Assert.Fail();
}
catch (T ex)
{
// This exception was expected.
return ex;
}
}
Using this helper method, you now can have your test method as follows:
[TestMethod]
public void Action_ThrowsException()
{
// test logic here
ArgumentException aex = Throws<ArgumentException>(()=>{
// Test logic here
});
Assert.AreEqual("Expected error message", aex.Message);
}
As you can see, this option provides you with higher flexibility as you can now validate other aspects of the exception explicitly.
FYI: The second solution is given as part of the xUnit framework.

NSubstitute: Assert does not contain definition for Throws

I am using the testing framework that comes with Visual Studio, along with NSubstitute to unit test a method that takes a system ID, and throws an exception if the system can't be found in the database...
public VRTSystem GetSystem(int systemID)
{
VRTSystem system = VrtSystemsRepository.GetVRTSystemByID(systemID);
if (system == null)
{
throw new Exception("System not found");
}
return system;
}
(In case this seems odd, there is a specific business case for this method that requires it to throw an exception, as returning a null system is not acceptable for its usage)
I want to write a test to check that an exception is thrown if the system doesn't exist. I currently have the following...
[TestMethod]
public void LicensingApplicationServiceBusinessLogic_GetSystem_SystemDoesntExist()
{
var bll = new LicensingApplicationServiceBusinessLogic();
try
{
VRTSystem systemReturned = bll.GetSystem(613);
Assert.Fail("Should have thrown an exception, but didn't.);
}
catch () { }
}
By not mocking the repository, the system returned by VrtSystemsRepository.GetVRTSystemByID() will be null, and the exception thrown. Whilst this works, it looks wrong to me. I wouldn't have expected to need a try/catch block in a test.
The NSubstitute docs have an example that implies I should be able to test this as follows...
[TestMethod]
public void GetSystem_SystemDoesntExist()
{
var bll = new LicensingApplicationServiceBusinessLogic();
Assert.Throws<Exception>(() => bll.GetSystem(613));
}
However, if I try this in my test code, I get Throws highlighted in red, with the error message "Assert does not contain definition for Throws"
Now, I'm not actually sure that the sample on that page covers my scenario, as the test code specifies that the method under test throws an exception, which I don't really understand, as I thought the idea of testing was to leave the method under test alone, and test what happens under various scenarios. However, even without that, I don't understand why the Assert.Throws method doesn't exist.
Anyone any ideas?
Edit: DavidG pointed out that Assert.Throws is probably part of NUnit, not the MS framework, which would explain why it's not recognised. If so, is the way I'm currently testing the right way to do it?
As mentioned by DavidG The referenced documentation is using NUnit for assertions.
If not using that framework you can use the ExpectedExceptionAttribute Class
[TestMethod]
[ExpectedException(typeof(<<Your expected exception here>>))]
public void GetSystem_SystemDoesntExist() {
var bll = new LicensingApplicationServiceBusinessLogic();
bll.GetSystem(613);
}
which would fail if the expected exception is not thrown.

How could I allow Assert in Unit Test with [ExpectedException(typeof(ArgumentException))]?

I found, that simple Unit test
[TestMethod()]
[ExpectedException(typeof(ArgumentException))]
public void UT_UU()
{
}
gives fault with
Message: Test method did not throw expected exception
System.ArgumentException.
I successfully use [ExpetedException] to test error message output, however, any checking with Assert of side variables in the Unit Test gives it faulted.
Could I somehow decorate my test to avoid it? Or it is "political error"?
with [ExpectedException(typeof(ArgumentException))] your unit test will pass only when the method under test generates an exception of similar type you specified. In your case ArgumentException else it will fail with the message you have posted.
So essentially, you will have to fail your test method with that injection by means of injecting that exception. Currently your test method doesn't throw the expected exception (it actually doesn't perform anything)
For example below test method would pass
[TestMethod()]
[ExpectedException(typeof(ArgumentException))]
public void UT_UU()
{
throw new ArgumentException();
}
Using Assert statements in the body of the test that is decorated with ExpectedException doesn't really fit the pattern well if your test is isolated enough to only be testing one thing. If you follow the "Arrange, Act, Assert" pattern, then Assert is in this case handled by the ExpectedExceptionAttribute itself and the last line of code in the body of the test itself would be the "Act" because it should cause the exception to occur. If you need to know something more specific about the thrown exception to ensure that the system is meeting behavioral expectations, then I would use a try/catch within the test itself to be able to provide more granular inspection:
[TestMethod]
public void UT_UU()
{
// Arrange
var subject = new Foo();
try
{
// Act
subject.Bar();
}
catch(ArgumentException ae)
{
Assert.AreEqual("my message", ae.Message);
return;
}
catch(Exception e)
{
Assert.Fail("Thrown exception was of wrong type"); // would provide more detail here
}
Assert.Fail("exception should have been thrown");
}
Obviously just using ExpectedException is nice because it lets you right a very clean test without extra noise. Alternatively, if you were going to re-use this type of test you could write a derivative of ExpectedException that lets you specify other characteristics (like Message) to check instead of just the type.

Why do my C# unit tests for exceptions fail if I use try/catch?

I'm using C# with nUnit to unit test my project and I'm trying to write some unit tests that ensure a certain Exception is called but my tests keep failing. After a lot of testing to work out the issue, I managed to identify the cause of it and created a small code example that shows my issuew below.
My unit test code:
[Test]
public void TestExceptionIsRaised()
{
var ex = Assert.Throws<UnauthorizedAccessException>(() => TestExceptionMethod());
StringAssert.Contains("Attempted to perform an unauthorized operation", ex.Message);
}
My method with no error checking:
public void TestExceptionMethod()
{
throw new UnauthorizedAccessException();
}
Now if I run that unit test with the above method... it throws the exception, nUnit detects it and the test passes. However, this code is a problem because it has no error handling and will crash if I release it like this.
So to solve that problem, I add some error handling into my method like this:
public void TestExceptionMethod()
{
try
{
throw new UnauthorizedAccessException();
}
catch (UnauthorizedAccessException ex)
{
// Do something about the error
}
}
But now when I run the unit test it fails with this error:
Expected: System.UnauthorizedAccessException
But was: null
So rather than having to choose between a unit test or proper error handling, I tried to re-throw the error like this:
public void TestExceptionMethod()
{
try
{
throw new UnauthorizedAccessException();
}
catch (UnauthorizedAccessException ex)
{
// Do something about the error
throw;
}
}
Finally, everything now works. The unit test passes every time. And my method can still catch the error and do something about it.
However from what I've heard it isn't good practice to keep re-throwing errors like this??? So I'm just wondering is my solution "correct"? Or is there a better way of unit testing for an exception without having to remove the try/catch blocks from the original method?
However from what I've heard it isn't good practice to keep
re-throwing errors like this???
There is nothing wrong with re-throwing exceptions. It's actually a best practice to re-throw the exception after your application runs some logging code without recovering your application state.
Or is there a better way of unit testing for an exception without
having to remove the try/catch blocks from the original method?
I'm not sure I understand what you mean. But if you expect an exception thrown from your method, there must be an exception for your test to pass no matter it's thrown directly or re-thrown. If you try/catch like this:
public void TestExceptionMethod()
{
try
{
throw new UnauthorizedAccessException();
}
catch (UnauthorizedAccessException ex)
{
// Do something about the error
}
}
The test will fail for sure because the exception is swallowed. The test cannot detect there was an exception inside the method.
Translating your test into English you are saying:
"I expect the method TestExceptionMethod to throw a
UnauthorizedAccessException. The message of which will contain a
string with more details."
By not throwing the exception this test will never pass. If you don't want the exception to be thrown you need to change your test. If you do want the exception to be thrown, what you're doing is correct.
Is this correct?
It depends. If what you're doing is an exceptional issue, then throw an exception. Let the program crash. Fix the bug.
On the other hand if this is something that is to be expected such as bad user input, boundary conditions, handle it if you can. If you cannot recover from the problem then throw an exception.
Exception handling done poorly can make debugging more difficult especially when the exception is swallowed, or when a new exception instance is created and thrown losing the stack trace of the original exception. Sometimes the latter is desirable when you need a more abstract exception higher up the call stack, in which case you can add the original exception as an inner exception to keep access to the stack trace.

Unit test throws exception but cannot see the message

Hi all I'm making a Chess AI as hobby project with Test Driven Development. But for some reason the only message my tests give is "test has thrown an exception: ...". And therefore omitting the only thing that matters. Instead of directly reading the error I now have to right-click and view test result details. I have tried adding and removing columns, but I cannot get the whole message to be shown directly.
Can VS2010 be setup so the exception message gets shown directly for each unit test?
edit: I am using standaard VS unit tests:
[TestClass]
public class MiniMaxTest
{
[TestMethod]
public void TestConstructor()
{
throw new Exception("Must I click view details to see this?");
}
}
Why these questions? You guys can reproduce these things. Debug or Run tests give the same messages:
No, I don't believe you can configure VS to show it differently. The IDE shows the first line of the exception message, and there's a newline character in the full message text, so you'll need to click through to the details to view the whole thing.
What you can do however, is abuse the MSTest framework to show your own messages.
MS Test Assertions are all implemented by throwing exceptions. All the MS Test Assert functions throw exceptions which are derived from UnitTestAssertException. Visual studio has special handling for these kinds of exceptions.
For example: If you write this test:
[TestMethod]
public void AllAboard()
{
throw new AssertFailedException("Failboat");
}
AssertFailedException is the standard base class for most other assertion failures.
You'll note that VS2010 does not print the generic "test threw an exception" message, instead it just prints "Failboat".
Now what you can do is surround your tests in things that convert normal exceptions into AssertFailedException and then you can print whatever messages you like.
[TestMethod]
public void TestStuff()
{
try
{
string o = null; // pretend this came from some real code
var x = o.Split(new[] { ',' }); // this will throw NullRefException
}
catch (Exception e)
{
throw new AssertFailedException(e.Message, e);
}
}
Of course, I wouldn't recommend actually doing this... it's verbose, and more importantly, you lose the call stack... but hey, now you've got one more tool in your toolbelt

Categories

Resources