views:

184

answers:

2

So I have a method i'm unit testing via MSTests to ensure it throws an exception properly on bad data coming in.

However that method has Debug.Assert so it's caught while debugging if it happens during an actual test where I'm in debug mode trying to find a bug.

So the unit tests fail at being automatable when run in this way because the Assert statement comes up with Abort, Retry, Ignore. All 3 situations are important:

I'm debugging and looking for a problem, so I want the Debug.Assert to be available. The code should have the proper guard clause so if it happens in production an exception is thrown. My unit test should be completely automated and run without manual clicking.

What's the work around?

A: 

Instead of calling Debug.Assert directly, you could call a wrapper method that checks whether a debugger is attached before invoking Debug.Assert. (Obviously, it should throw an exception if there is no debugger attached.) e.g.:

[Conditional("DEBUG")]
public static void Assert(bool condition)
{
    if (Debugger.IsAttached)
    {
        Debug.Assert(condition);
    }
    else
    {
        throw new AssertionException();
    }
}
Nicole Calinoiu
This would still call Debug.Assert when I'm running my unit tests locally, right? That's not what I want.
Maslow
It will call Debug.Assert when you are debugging your tests locally, but not when you are running them without a debugger attached.
Nicole Calinoiu
right, but when I'm running my unit tests locally there is a debugger attached, so it would interfere with the testing.
Maslow
No, when you use one of the "Run tests" commands in Visual Studio to run tests under MSTest, there is no debugger attached. There is only a debugger attached when you use one of the "Debug tests" commands.
Nicole Calinoiu
oh, very nice. Would this function need to be defined in one of the active projects? for isntance if I compile a dll that has this function as release mode, will it recognize the assembly calling it is in DEBUG and act accordingly?
Maslow
It can be contained in either one of your projects or a referenced assembly. As with Debug.Assert, calls to the method will not be included in compiled code unless that code is compiled with the DEBUG switch. For more information, see http://msdn.microsoft.com/en-us/library/system.diagnostics.conditionalattribute.aspx.
Nicole Calinoiu
I get a compile error with this code: Cannot access internal class 'Contract' here. I also don't like needing to write this function somewhere into every single assembly I want to unit test. Additionally AssertFailedException would require I reference the Microsoft Unit test framework in my actual production code.
Maslow
The "AssertionException" in the sample was intended to be a custom exception, not the code contracts AssertionException. If you don't want to create a custom exception, change it to whatever exception you like.You do not need to include the function in every assembly. Put it in whatever shared library assembly seems suitable.
Nicole Calinoiu
A: 

So far when the unit tests are run locally Nicole's solution didn't do what I wanted, because I was intentionally passing in values that would trip the Asserts to make sure exceptions are thrown, interferes with running the unit tests automated locally. I suppose it would work if I was willing to accept the [Conditional("DEBUG")] compilation attribute and run the unit tests in release mode. If I want that behavior I can provide a test level(or assembly level) wrapper with the [Conditional("DEBUG")] in the testing assembly, but this one I can utilize from a pre-compiled reusable class library.

This gets pretty close to what I want, with the additional requirement of calling Trace.Listeners.Clear(); in my test suite.

/// <summary>
/// To suppress UI assert messages use:
/// Trace.Listeners.Clear();
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assertAgainst"></param>
/// <param name="condition"></param>
/// <param name="exception"></param>
/// <returns></returns>
public static T Assert<T>(this T assertAgainst, Func<T,bool> condition,Func<Exception> exception)
{
    var conditionMet = condition(assertAgainst);
    if (Debugger.IsAttached)
        Debug.Assert(conditionMet);

        //assertAgainst.Assert(x => x != null, () => new NullReferenceException());
        if (!conditionMet)
            throw exception();

    return assertAgainst;
}
Maslow