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?

Maslow
  • 18,464
  • 20
  • 106
  • 193

4 Answers4

3

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
  • 18,464
  • 20
  • 106
  • 193
  • According to Ben Hall, you could also try clearing your trace listeners in the App.Config of the test project: http://blog.benhall.me.uk/2008/05/disable-systemdiagnosticsdebugassert.html – rohancragg Apr 26 '11 at 13:25
2

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
  • 20,843
  • 2
  • 44
  • 49
  • This would still call Debug.Assert when I'm running my unit tests locally, right? That's not what I want. – Maslow Feb 02 '10 at 16:12
  • 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 Feb 02 '10 at 16:29
  • right, but when I'm running my unit tests locally there is a debugger attached, so it would interfere with the testing. – Maslow Feb 02 '10 at 16:34
  • 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 Feb 02 '10 at 18:56
  • 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 Feb 02 '10 at 20:20
  • 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 Feb 03 '10 at 15:05
  • 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 Feb 05 '10 at 16:11
  • 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 Feb 05 '10 at 19:23
1

I just came across the same issue but then I realized that this scenario shouldn't ever come up unless there is an error in your code. In which case I wouldn't really mind the assert popping up. Here is my chain of reasoning:

  1. A Debug.Assert should only be hit when a function's preconditions are not met (or if there is an error in the function it self).
  2. A unit test should check a function's post condition.
  3. A function only needs to produce its post conditions if all of its preconditions are met so there is no point in writing a unit test for a function that passes it invalid preconditions.
rob
  • 17,995
  • 12
  • 69
  • 94
0

This assert won't interfere with automated tests, it will fail the test case if the assert fails, but execution will continue.

Sean
  • 11
  • 1
  • 4
  • 2
    sure, but that requires I take asserts out of the model code and only keep them in the unit tests. i'm looking for a way to do both due diligences. – Maslow Feb 03 '11 at 14:26