One of the things that the masters of Unit Testing say is that tests should always be repeatable and deterministic. No matter when, or how many times you run a test, if the System Under Test stays the same, the results of the test should stay the same. So I found myself a bit confused recently when some tests went from reliable to unreliable results. Turns out the tests were right, and they were pointing out a silly bug I'd introduced by accident...
public class OriginalClass { public int Value { get; set; } }
That class had a couple of tests which created an object, fiddled with some values and asserted that the values ended up right. Again, stripping it back to the basics:
[TestMethod] public void FirstTest() { var sut = new OriginalClass(); sut.Value += 1; Assert.AreEqual(1, sut.Value); } [TestMethod] public void SecondTest() { var sut = new OriginalClass(); sut.Value += 2; Assert.AreEqual(2, sut.Value); }
Unsurprisingly those tests pass:
Over the course of a few further cycles of "write a test, change some code, run the tests" I found myself realising that the class at the centre of this issue could be used more simply with some default state. That lead me to add a static "Default" to the class and revise the tests a bit:
[TestClass] public class IncorrectClassTests { [TestMethod] public void FirstTest() { var sut = IncorrectClass.Default; sut.Value += 1; Assert.AreEqual(1, sut.Value); } [TestMethod] public void SecondTest() { var sut = IncorrectClass.Default; sut.Value += 2; Assert.AreEqual(2, sut.Value); } }
But at this point I noticed that the tests became unreliable. Clicking "run all tests" lead to:
But running only the failed tests succeeded:
I scratched my head and re-ran those tests a few times, but the results were consistently inconsistent. What could cause that to happen?
The key problem was that change to include a default state of the class. I suspect you can probably spot the error:
public class IncorrectClass { public static readonly IncorrectClass Default = new IncorrectClass() { /* some initialisation */ }; public int Value { get; set; } }
My silly mistake here is that because
IncorrectClass
is a class, it's accessed by reference. And since it's also a static, that means that all of the code is referring to the same instance of
IncorrectClass
. Hence if you run a single test the object is created, and passes the test and then destroyed. But if you run multiple tests, the object is created and reused. So the initial state is wrong for the second (and subsequent) tests...
A bit of a facepalm moment there. What I should have used was more like:
public class CorrectClass { public static CorrectClass Default { get { return new CorrectClass() { /* some initialisation */ }; } } public int Value { get; set; } }
That ensures that the "default" is created with the correct state each time.