views:

196

answers:

2

I'm using NUnit to write unit tests for a libary a colleague of mine has written. His library contains a lot of Debug.Asserts which triggers on invalid input. When I'm writing the unit tests and give invalid input to his library, his Debug.Assert throws up a message box complaining about the bad input.

I feel that it's a good thing that his library throws up an assert on invalid input, but at the same time I want the unit tests to cover bad input. But when I do this, the message box shows up and I have to manually click OK to continue with the remaining unit tests.

In case it isn't clear, my problem is that the unit test process stops on the Debug.Assert. People are supposed to run their unit tests prior to any checkin and it's supposed to be automatic and should not throw up messages unless a test has failed..

What's the "best" approach in this case?

+1  A: 

Take a look at the MSDN documentation for the Debug.Assert method. Specifically under "Remarks", it explains how you can disable the UI:

<configuration>
  <system.diagnostics>
    <assert assertuienabled="false" logfilename="c:\\myFile.log" />
  </system.diagnostics>
</configuration>

Therefore I'd suggest that the application config file has this by default and your colleague switches on Assert to UI whenever he feels it useful to do so.

Rob
+2  A: 

As Henk already noted, suppressing the UI is useless, because you want your that code to fail. When you don't want to change your code, you can write a custom trace listener that throws an exception, as follows:

public class ProductionTraceListener : DefaultTraceListener
{
    public override void Fail(string message, string detailMessage)
    {
        string failMessage = message;

        if (detailMessage != null)
        {
            failMessage += " " + detailMessage;
        }

        throw new AssertionFailedException(failMessage);
    }
}

[Serializable]
public class AssertionFailedException : Exception
{
    public AssertionFailedException() { }
    public AssertionFailedException(string message) : base(message) { }
    public AssertionFailedException(string message, Exception inner) 
        : base(message, inner) { }
    protected AssertionFailedException(SerializationInfo info,
        StreamingContext context) : base(info, context) { }
}

And you can register it as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <trace>
      <listeners>
        <clear />
        <add name="Default"
          type="[Namespace].ProductionTraceListener, [Assembly]" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

As you already might expect from the trace listener's name ProductionTraceListener, I use that thing in my production environment (web applications), and not in my unit tests. While you can use this trick in your unit tests, I advice you to change your code. IMO you should only use asserts for code paths that should never run, and if they do, the test should fail. In your situation you want to have a succeeding test when a assert fails, which is counterintuitive.

My advice is to change the code and use normal if (x) throw ArgumentException() checks for preconditions (or use CuttingEdge.Conditions) and use those asserts only for code paths that should never run. Also try using Trace.Assert instead of Debug.Assert, because you also want those asserts to be checked in your production environment. When you've done that you can use the ProductionTraceListener in your production environment, and this UnitTestTraceListener in your unit tests.

public class UnitTestTraceListener : DefaultTraceListener
{
    public override void Fail(string message, string detailMessage)
    {
        string failMessage = message;

        if (detailMessage != null)
        {
            failMessage += " " + detailMessage;
        }

        // Call to Assert method of used unit testing framework.
        Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail(
            failMessage);
    }
}

Good luck.

Steven
This would look good for Trace.Assert(), but the OP mentions Debug.Assert().
Henk Holterman
Plus, you want the code to fail *in the unit tests* when running them as part of an automated build proess. The Asserts are for the developers benefit whilst running the code.
Rob
Steven
Steven, Debug.Assert() is bad because it makes the Debug build behave different from the Release build. Which behavior do you want to test?
Henk Holterman
Henk, that's exactly why I advice to use `Trace.Assert`. Having a debug build behave differently is a bad thing. I think we think the same about this.
Steven