0

First off, this is very similar to this question.

What I'm trying to do is run a test that passes if the program doesn't throw a specific exception, but if the program does that specific exception, I want NUnit to put a message in the test runner explaining the likely cause of this specific exception.

Here's the situation, I'm doing the bowling score kata and I thought, what should happen if someone inputs a value for the 22nd roll (the highest number of rolls should every be 21 assuming a spare in the last frame).

My solution uses an array of 21 ints, and I've created a test that inputs 22 ints.

When I look at this page I see the option for this syntax:

Assert.DoesNotThrow( TestDelegate code, string message );

Here's what I think the line should look like, but I know (and the compiler is complaining) that using typeof is not going to get me the test delegate code.

[Test]
public void ShouldIgnore22ndRoll()
{
     Assert.DoesNotThrow(typeof(System.IndexOutOfRangeException), "There should never be more than 21 rolls in a game");
     RollAllTheSameNumber(1, 22);
}

Bottom line, I know what the problem is if I see this exception, and I want to tell my future me what that problem is (by populating the message that gets sent to the testrunner).

Any other exception, I'm going to have to figure out, but this exception is one I've already done the thinking for.

How would I write this assert?

Edit:

Here's the variable definitions:

int rollNumberDuringDataInput = 0;
const int AllowableNumberOfRolls = 21;

Here is the fixed code (silently ignoring any rolls past number 21):

public void AddPins(int pins)
{
    if (rollNumberDuringDataInput < AllowableNumberOfRolls)
    {
        rolls[rollNumberDuringDataInput++] = pins;
    }
}

Here is the code that will cause the test to fail by allowing more than 21 rolls:

public void AddPins(int pins)
{
    rolls[rollNumberDuringDataInput++] = pins;
}

Currently, this is the message generated in the test runner: Message: System.IndexOutOfRangeException : Index was outside the bounds of the array.

What I want the message to be for this exception (and only this exception Message: System.IndexOutOfRangeException : There should never be more than 21 rolls in a game.

If someone removes the guard logic, I want the message in the test runner to tell me the business logic behind why that guard is there.

If you've never dealt with a whack-a-mole series of bugs (where one fix exposes an old vulnerability in the code, and that fix, exposes a different vulnerability, and that fix exposes the first error again; repeat) count yourself lucky.

I saw, what I thought, was a method to trap for a specific undesirable condition, that if it ever came back, we could quickly inform the breaking party, why their fix broke the code.

Malcolm Anderson
  • 967
  • 1
  • 6
  • 13
  • 1
    You are essentially testing for **two** different scenarios in **one** test which is bad test design because when it fails, atomic statements are insufficient to describe _which_ case it is. Imagine testing a bowl containing 5 apples. You make a test stating that you should be able to take 5 apples from the bowl, then take a **6th** but ignore the fact that there are no more apples. Kinda strange. You should split your test into say `Test2ndRollPass()` and `Test22ndRollFail()` the latter containing say a `Assert.Throws(...)` statement. –  Jun 30 '19 at 07:12
  • 1
    ...additonally, if for some unknown reason during the {1..21} rolls in the bowels of the code an unforseen `IndexOutOfRangeException` is thrown, it will now be ignored because your test is assuming the error came from the `22nd roll` which is `incorrect` –  Jun 30 '19 at 07:16
  • (added additional info above) An exception is not a passing condition, it should still fail, I just want to change the message in the test runner from just the exception, to an exception, plus a reason. In this case, someone has to specifically come back later, and change the code... and as long as all the inputs are sanitized so that 21 rolls never happens, there will be no problem, but years later, when we get a new vendor that doesn't sanitize their inputs we have a problem we have to debug. I want to leave a message, saying, "you took off guard rails we want left in the code" – Malcolm Anderson Jun 30 '19 at 17:45

1 Answers1

1

From your description

If I understand what you are asking then you should rephrase what you tell your future self.

[Test]
public void ShouldIgnore22ndRoll() {    

     Assert.Throws<System.IndexOutOfRangeException>(() => RollAllTheSameNumber(1, 22), "More than 21 rolls in a game should fail.");

}

Any other exception other than the one expected would mean that the code does not behave as expected.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Hi Nkosi, no this is a test that I'm adding that I then added code to cover, so the code will not throw an exception, unless someone changes the code to remove the guard rails. I don't want the exception, but if it happens, then I already know what the problem is and I want to tell the developer who broke the code, why their change broke the code. – Malcolm Anderson Jun 30 '19 at 17:19