Yes you can have loops in unit test, but with caution. As mentioned by Alex York, loops are acceptable if you test one thing; i.e. one expectation.
If you use loops, then I recommend that you must do two things:
- As mentioned above, test for a non-empty iteration set. Iterating over an empty set is a false positive. False positive results are bane of all automated testing because nobody double checks a green result.
- Include a test description that describes the current iteration. At a minimum include the iteration index.
Here is an example from my testing of the Greater Than property of an object.
[Test]
public void TestCompare_XtoY_GreaterThan()
{
int numObjects = mOrderedList.Count;
for (int i = 1; i < numObjects; ++i)
{
for (int j = 0; j < i; ++j)
{
string testDescription = string.Format("{0} is greater than {1} which implies\n {2}\n is greater than\n {3}"
, i, j
, mOrderedList[i], mOrderedList[j]
);
Assert.IsTrue(0 < mOrderedList[i].CompareTo(mOrderedList[j]), testDescription);
Assert.IsTrue(0 < mOrderedList[i].Compare(mOrderedList[i], mOrderedList[j]), testDescription);
Assert.IsTrue(0 < mOrderedList[j].Compare(mOrderedList[i], mOrderedList[j]), testDescription);
Assert.Greater(mOrderedList[i], mOrderedList[j], testDescription);
}
}
}
I test that my ordered list is non-empty in the test setup using:
[SetUp]
public void GeneralTestSetup()
{
// verify the iterated sources are not empty
string testDescription = string.Format("The ordered list of objects must have at least 3 elements, but instead has only {0}.", mOrderedList.Count);
Assert.IsTrue(2 < mOrderedList.Count, testDescription);
}
I have multiple Asserts even within my loop, but all of the asserts are testing the single expectation:
if i > j then mOrderedList[i] > mOrderedList[j]
The test description at the iteration level is so you get Failures such as:
10 is greater than 9 which implies
TestActionMethodInfo: [Actions.File, Version=1.0.446.0, File, VerifyReadOnly]
is greater than
TestActionMethodInfo: [Actions.File, Version=1.0.446.0, File, Write]
Expected: True
But was: False
instead of just:
Expected: True
But was: False
The question/debate about my code above:
Is am I testing one thing?
I am asserting on 4 different comparison methods within the object which could be argued as testing 4 things not one. The counter is greater than is greater than is greater than and that all of the methods which make that evaluation should be consistent.