If you’ve been writing unit tests for some time or seen a good presentation on how to write unit tests you probably heard the “One assert per test” rule. There are real benefits in having only one assert in each tests – you get a focused tests, it’s easier to understand what caused the test to fail as so on.
However there are several reasons to use more than one assert in a test – not only when you want to check several properties of the same result.
The problem is that it doesn’t matter whether or not it’s ok to write several asserts in one test when a test fails you’ll be stuck with a partial view – and that’s due to the assertion implementation – it throws exception on failure, and so the first assert that fails throws an exception leaving you completely oblivious to the result of the asserts following it (please excuse the overly simplified example):
[TestMethod] public void SimpleTest() { var result = 2 + 2; Assert.IsInstanceOfType(result, typeof(double)); Assert.AreEqual(4, result); }
And so if the first assert fails I have no clue what is the state of the 2nd assertion.
I came across this idea while reading Multiple mocks, asserts, and hidden results:
If test frameworks did NOT throw exceptions form asserts\mocks, but instead gathered all of their data before ending the test as fail\pass, this would not be needed.
Obviously this feature is not yet supported and so a solution is up to us. Roy links to a tool that uses code generation to create different tests for each assert – how cool is that! But I thought that there bound to be a simple way to run multiple asserts and only output the fail results at the end of the test run – and there is:
public class AssertAll { public static void Execute(params Action[] assertionsToRun) { var errorMessages = new List(); foreach (var action in assertionsToRun) { try { action.Invoke(); } catch (Exception exc) { errorMessages.Add(exc); } } if(errorMessages.Any()) { var separator = string.Format("{0}{0}", Environment.NewLine); string errorMessageString = string.Join(separator, errorMessages); Assert.Fail(string.Format("The following condtions failed:{0}{1}", Environment.NewLine, errorMessageString)); } } }
And now our test looks like this:
[TestMethod] public void SimpleTest2() { var result = 2 + 2; AssertAll.Execute( () => Assert.IsInstanceOfType(result, typeof(double)), () => Assert.AreEqual(4, result)); }
It’s not pretty but it works, and you get an error message for all the asserts that failed.
As always remember that “just because you can – doesn’t mean you should”, there are instances where multiple asserts in a single tests means that you’re testing too much in the same test – and it’s up to you to decide if it’s a good idea.
When to use the SetUp attribute
Labels: .NET, C#, MSTest, Unit tests