9

I've been trying to wrap my head around this issue: How to create a unit test that tests if a function aborts because a Debug.Assert (from System.Diagnostics) fails, marking it as passed when it does this and as failed if it doesn't.

I knowNUnit has the feature [ExpectedException(typeof( ArgumentException ) )], but I cannot seem to find out what kind of exception it is from the MSDN website. Intuition would say it might be something like an AssertionException, and that one does exist... but is part of the NUnit framework. I guess that's the exception NUnit assert throw around. I might be able to just nuke it by using:

[ExpectedException(typeof(Exception))]

But this gives rise to the problem that the standard windows debugging window shows up. In my search I came across ways to remove this window from showing up at all, but this feels like taking a butcheringknife to the surgerytable where you normally use a scalpel. Because I do want to be able to see this window when something unexpected happens, when I would execute my program.

I guess there is the workaround of replacing the Debug.Assert method with the NUnit counterpart (I'm still early in my project, so it's not a too big of a refactoring), but I assume a lot of programmers stick with Debug.Assert functionality as it's standard in .NET.

As such, I'd like to know how to 'assert' Debug.Assertion failures, without having to 'murder' the Windows debugging screen from my project?

To have an concrete example from a contract in my code, there is a sample below. For those it might seem familiar, it is the To-Wound table from Warhammer 40K tabletop wargame written as a function.

static public int assaultToHit(int _attacker_WeaponSkill,
            int _defender_WeaponSkill)
        {
            //Preconditions
            Debug.Assert(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10,
                "Weapon Skill stat must be in range [1,10]");
            Debug.Assert(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10,
                "Weapon Skill stat must be in range [1,10]");

            int target;
            if (_attacker_WeaponSkill > _defender_WeaponSkill)
            {
                target=3;
            }
            else if (_defender_WeaponSkill >= (_attacker_WeaponSkill + _attacker_WeaponSkill + 1))
            {
                target=5;
            }
            else
            {
                target=4;
            }

            //postconditions
            Debug.Assert(target >= 3 && target <= 5,
                "To hit target for assault must be in range [3,5]");

            return target;
        }

The function to test the preconditions would be in the line of something like this:

    [TestCase(-1,2)]
    [TestCase(1, -2)]
    [TestCase(-1, -2)]
    [TestCase(11, 2)]
    [TestCase(1, 20)]
    [TestCase(11, 20)] 
    [ExpectedException(typeof(Exception))]
    public void testContract_AssaultToHit(int _attacker_weaponskill, 
        int _defender_weaponskill)
    {
        Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
            _defender_weaponskill);
    }
Xilconic
  • 3,785
  • 4
  • 26
  • 35
  • 1
    That isn't a workaround, it's what you should be doing. Debug.Assert only fires when compiling with debug on. It's an optional investigative tool. Absolutely nothing in your released code should rely on it fro normal operation, so it's pointless to unit test for it. – Tony Hopkinson Feb 10 '12 at 16:02
  • @TonyHopkinson You've got a point there. But to the same extent, one could use Trace.Assert instead of using NUnit Asserts. In that case you wouldn't need to add the NUnit framework .dll along with the rest of your code right? In that case the question would remain the same I think: how can you test that the Trace.Assert/Debug.Assert fire correctly using NUnit? – Xilconic Feb 13 '12 at 07:59
  • Never thought about it to be honest. An Assert is an Assert, I wouldn't try to test it any more than I would writelines, alerts and MessageBoxes while I was trying to figure out why my code wasn't doing what I thought it should. Sort of a who watches the watchdogs question isn't it? Interesting question, but I'm not seeing a lot of use for the answer... – Tony Hopkinson Feb 13 '12 at 15:03
  • @TonyHopkinson I guess this might be something up to personal taste. For me, when I do design by contract I want to unit test it too as I want my contract to be correctly implemented when delivered. I want to assert that it works as I intend it to work, meaning I have to test both the 'happy path', the cornercases and the 'violation part'. That just how I like to deliver software. If someone would perceive a contract just as a programming aid, then it might indeed be just overkill and additional work. – Xilconic Feb 14 '12 at 07:30
  • The contract would be to throw exceptions, not rely on debug flags and trace logs, they are for devs not customers, even if the customer is a dev. I suppose if the contract as in boyo paying me specified trace and asserts as part of the product, then it would be different. – Tony Hopkinson Feb 14 '12 at 17:59
  • 1
    Note that a Debug.Assert exception is actually thrown by a TraceListener, not the assert itself. You could install a listener that throws an assert, and react to that. – yoyo Jun 11 '14 at 23:13

3 Answers3

5

It seems you have used wrong facilities to control an application flow in case of an error. Debug.Assert() should not be used to drive an application logic flow.

Unit Test should cover a real test case and it seems either you're trying to implement wrong test case or you need to throw an exception/etc rather than using Debug.Assert(). You can share some code so it would be possible give you some specific advices.

Anyway you can read MSDN about how-to add a custom trace listener and intercept Assert calls.

Useful links:

sll
  • 61,540
  • 22
  • 104
  • 156
  • I have provided some sample code in my post. I'm currently using Debug.Assert to verify my code contract, as per Design by Contract methodology. I'd like to write NUnit tests to test/verify my contract, that it indeed my contract kicks in when I'm not fulfilling it. I'm using assertions instead of exceptions here to keep the code easier to read and still achieve the same goal. The creation of a Custom Trace Listener still feels like 'murdering' the whole "Abort, Retry, Ignore" window. I don't want to redirect _all_ assertion, maybe only the ones I've created for unit testing. – Xilconic Feb 13 '12 at 08:37
  • @Xilconic: I believe you shall throw `ArgumentNullExcention`rather than usign `Debug.Assert()` for argument validation error – sll Feb 13 '12 at 11:24
  • Yeah, I have been looking at what the general concensus is about when to use an assert and when to throw an exception. In http://stackoverflow.com/a/117247/605538 is recommended to use exceptions for public functions, and asserts for 'private' affairs. I actually agree with his reasoning, so I'm going with your and Dima's suggestion and change from assertion to throwing an exception. – Xilconic Feb 13 '12 at 14:55
3

From https://stackoverflow.com/a/117247/605538 one can find a recommendation to use exceptions for public interfacing, while using assertions to verify internal code. When unit-testing preconditions of a function (and a similar thought can be applied for post-conditions, altough I personally would prefer to use assertions there), one can therefore be recommended to use exceptions instead of using assertions.

Using exceptions instead of would result in a sample like this:

static public int assaultToHit(int _attacker_WeaponSkill,
        int _defender_WeaponSkill)
    {
        //Preconditions
        if(!(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10))
        {
            throw new ArgumentOutOfRangeException("Attackers WeaponSkill must be in range [1,10]");
        }
        if(!(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10))
        {
            throw new ArgumentOutOfRangeException("Defenders WeaponSkill must be in range [1,10]");
        }

        ...
        //rest unchanged
    }

Coupled with the following NUnit test:

[TestCase(-1,2)]
[TestCase(1, -2)]
[TestCase(-1, -2)]
[TestCase(11, 2)]
[TestCase(1, 20)]
[TestCase(11, 20)] 
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void testContract_AssaultToHit(int _attacker_weaponskill, 
    int _defender_weaponskill)
{
    Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
        _defender_weaponskill);
}

Sidebar [12 June 2014]: I've been recently coming to the opinion that you should not have to test precondition violations in the context of Design by Contract. If you design with DbC in mind, you basically state the following: "If you call a method and meet its preconditions, you are guaranteed a certain behavior. If you do not meet the method's preconditions, you can expect undefined behavior.". In the broadest sense of the term 'undefined behavior', this means that you cannot expect any sort of state. You might get exceptions, maybe nothing happens at all and maybe your hard disk gets formatted (well... you get the point ;) ). As such, you cannot test for 'undefined behavior'.

What you can test, is your defensive programming that makes sure that a precondition failure does not cause the function to operate, for example by throwing an Exception. This behavior is testable.

Community
  • 1
  • 1
Xilconic
  • 3,785
  • 4
  • 26
  • 35
1

For your particular case I would recommend you to have a look into Microsoft Code Contracts, as the specific assert case you have is targeted at checking input contracts for your functions.

If Code Contracts is too big for you, I would recommend that you make your contracts throw exceptions when implicit contracts are not adhered to, instead of asserting.

However Debug.Asserts are still valuable, but should be used in situations that are less-likely to happen, such as after a statement that makes a call returning null instead of the empty collection that you expect.

Casper Leon Nielsen
  • 2,528
  • 1
  • 28
  • 37
  • I've been working with Code Contracts for some time now, and I do agree with you. I've come to the opinion that using Code Contracts for precondition checking is superior to if-then-exception patterns. But of course, this is only an option if you with with C# like I'm doing now (or have an programming language with integrated support for Design by Contract). Otherwise, using the if-then-exception pattern is still the way to go. – Xilconic Jun 12 '14 at 06:19