I'm not sure whether I'd call it a bug or not, and here's why:
When you call "Singleton.Instance," it's not really retrieving a static item, it's instantiating something and returning that. Since it's never used anywhere else in the code except when it's being mocked, the real static field "_instance" will always be null and the "get" will always instantiate a new one. If you look in the Tracer utility when you run the tests, that's actually exactly what you see - the expectations are set on different instances of the SingletonClass object. For example, in a test that sets an expectation on SingletonClass.Instance.GetDateTime twice, you'll see that there are expectations set up around two different instances of the SingletonClass.
The reason it works when you order the statements, then, is because the mock setup is saying, "For the first instance retrieved, do this; for the second instance, do this; for the third instance, do that." If you change the order of the statements in the mock setup, the expectations on the instances are incorrectly set up - you'd be saying "on this instance, expect a call to GetDateTime" when in the executing code that instance is the one that gets "SetDateTime" called on it. The RepeatAlways doesn't work because it's setting the expectation on the instance of SingletonClass that gets first retrieved; in the subsequent calls to SingletonClass.Instance, you're working with different instances that the expectations aren't set on.
That said, I can see where logically you'd think this should work, and I agree, it appears that it should work. That's why I'm not sure if I'd call it a bug or not. Internally I can see why it doesn't work, but from the outside I can see why I might want it to.
Regardless, you can still make your tests work by setting up your expectations around a specific instance of the SingletonClass. I've updated the two tests that were failing so they pass the way you think they should. (I also updated the way the tests assert success/failure so it's not a manual check against the console output.)
[TestFixture]
public class ClassUnderTestUnitTests
{
TextWriter _testOut = null;
TextWriter _originalOut = null;
[SetUp]
public void SetUp()
{
this._originalOut = Console.Out;
this._testOut = new StringWriter();
Console.SetOut(this._testOut);
}
[TearDown]
public void TearDown()
{
Console.SetOut(this._originalOut);
this._originalOut = null;
this._testOut.Dispose();
this._testOut = null;
}
private string BuildAssertionDateString(DateTime date1, DateTime date2)
{
return String.Format("{0:dd-MMM-yyy HH:mm:ss}{2}{1:dd-MMM-yyy HH:mm:ss}{2}", date1, date2, Environment.NewLine);
}
[Test, VerifyMocks]
public void TestMethod_WhenCalled_WorksWithRepeatAlways()
{
SingletonClass concreteInstance = SingletonClass.Instance;
using (RecordExpectations recorder = RecorderManager.StartRecording())
{
SingletonClass mock = SingletonClass.Instance;
recorder.Return(concreteInstance).RepeatAlways();
concreteInstance.GetDateTime();
recorder.Return(new DateTime(1969, 1, 17)).RepeatAlways();
concreteInstance.SetDateTime(DateTime.MinValue);
}
ClassUnderTest testClass = new ClassUnderTest();
testClass.TestMethod();
Assert.AreEqual(this.BuildAssertionDateString(new DateTime(1969, 1, 17), new DateTime(1969, 1, 17)), this._testOut.ToString());
}
[Test, VerifyMocks]
public void TestMethod_WhenCalled_WorksWithRepeatedExpectation()
{
SingletonClass concreteInstance = SingletonClass.Instance;
using (RecordExpectations recorder = RecorderManager.StartRecording())
{
SingletonClass mock = SingletonClass.Instance;
recorder.Return(concreteInstance).RepeatAlways();
concreteInstance.GetDateTime();
recorder.Return(new DateTime(1969, 1, 17));
concreteInstance.GetDateTime();
recorder.Return(new DateTime(1969, 1, 17));
concreteInstance.SetDateTime(DateTime.MinValue);
}
ClassUnderTest testClass = new ClassUnderTest();
testClass.TestMethod();
Assert.AreEqual(this.BuildAssertionDateString(new DateTime(1969, 1, 17), new DateTime(1969, 1, 17)), this._testOut.ToString());
}
}
The major change there is that I'm getting the instance outside the mock setup, then I'm setting up my expectations against that specific instance.
You could probably also fix this by changing the implementation of SingletonClass to instantiate the _instance field in the static constructor rather than via lazy evaluation. Then in your mocks just let the static constructor run on the SingletonClass and you'll always be setting mocks up against the same instance.