chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
I upgraded from 6.2.5 to 7.0.6 in hopes that it would fix the issue below, but to no avail. When using multiple .WhenCalled().WillReturn() on the same lambda, the first value is returned twice, and only then are the rest of the values returned. The following test, demonstrating the issue, should pass but it fails:

[TestMethod]
public void Test()
{
   Isolate.WhenCalled(() => DateTime.UtcNow).WillReturn(DateTime.Parse("2000-01-01 08:00:00"));
   var d1 = DateTime.UtcNow;

   Isolate.WhenCalled(() => DateTime.UtcNow).WillReturn(DateTime.Parse("2000-01-01 08:00:40"));
   var d2 = DateTime.UtcNow;

   Isolate.WhenCalled(() => DateTime.UtcNow).WillReturn(DateTime.Parse("2000-01-01 08:01:20"));
   var d3 = DateTime.UtcNow;

   Assert.AreEqual(DateTime.Parse("2000-01-01 08:00:00"), d1);
   Assert.AreEqual(DateTime.Parse("2000-01-01 08:00:40"), d2);
   Assert.AreEqual(DateTime.Parse("2000-01-01 08:01:20"), d3);
}
asked by allon.guralnek (10.6k points)

2 Answers

0 votes
Hi,

The reason the original test seems to return wrong values due to a mix between Arrange & Act sections. As a solution, you can set a sequence once that precedes the Act, for example:
Isolate.WhenCalled(() => DateTime.UtcNow).WillReturn(DateTime.Parse("2000-01-01 08:00:00"));
Isolate.WhenCalled(() => DateTime.UtcNow).WillReturn(DateTime.Parse("2000-01-01 08:00:40"));
Isolate.WhenCalled(() => DateTime.UtcNow).WillReturn(DateTime.Parse("2000-01-01 08:01:20"));

var d1 = DateTime.UtcNow;
var d2 = DateTime.UtcNow;
var d3 = DateTime.UtcNow;

Assert.AreEqual(DateTime.Parse("2000-01-01 08:00:00"), d1);
Assert.AreEqual(DateTime.Parse("2000-01-01 08:00:40"), d2);
Assert.AreEqual(DateTime.Parse("2000-01-01 08:01:20"), d3);


If it is necessary to control the behavior during the act, you can do it using the DoInstead feature. In order to do so, you can set a local variable in the test that hols the current required value and point to it with the DoInstead. During the execution you can change the value of that variable and control the current behavior. For example:
var f1 = DateTime.Parse("2000-01-01 08:00:00");
var f2 = DateTime.Parse("2000-01-01 08:00:40");
var f3 = DateTime.Parse("2000-01-01 08:01:20");

DateTime fakeTime = new DateTime();
Isolate.WhenCalled(() => DateTime.UtcNow).DoInstead(c => fakeTime);

fakeTime = f1;
var d1 = DateTime.UtcNow;

fakeTime = f2;
var d2 = DateTime.UtcNow;

fakeTime = f3;
var d3 = DateTime.UtcNow;

Assert.AreEqual(DateTime.Parse("2000-01-01 08:00:00"), d1);
Assert.AreEqual(DateTime.Parse("2000-01-01 08:00:40"), d2);
Assert.AreEqual(DateTime.Parse("2000-01-01 08:01:20"), d3);


Please let me know if it resolves the issue.

Regards,
Alex,
Typemock Support
answered by alex (17k points)
0 votes
Yes, I'm already using the .DoInstead() workaround. But are you saying that the behavior is correct and is not a bug? I certainly wasted a good chunk of my time figuring out why it wasn't working, so if the way I used it wasn't correct at least provide an error or warning and guide the user to the correct usage as it is in no way obvious that this shouldn't be done.

In any case, from my perspective the original test was following the AAA pattern. The component that was being tested was time-sensitive, and so in order to advance the time inside the test ("virtual time") without resorting to Thread.Sleep() calls (which dramatically increases the time it takes the tests to run), I used Isolator to fake DateTime.UtcNow. I wrapped it inside an FakeDelay(TimeSpan) method so that I could write:

// Arrange
initialize and fake stuff;
create expected values;

// Act
do something;
FakeDelay(TimeSpan.FromSeconds(5));
do some more;
FakeDelay(TimeSpan.FromMinutes(30));
retrieve actual value;

// Assert
Verify method calls;
Assert actual is equal to expected;


Moving all the delay to the Arrange section, it would make the test very hard to read (since you'd have to perform mental gymnastics to figure out which method will receive each faked time), or even break the test in some cases (i.e. DateTime.UtcNow is called multiple times during each work statement and you want the DateTime to remain the same until the next time you change it). It seems to me that advancing the virtual time belongs in the Act section and is in no way related to the Arrange section. So either it's a bug or it's an incorrect usage or limitation of Isolator, in which case you should provide some kind of indication of that.
answered by allon.guralnek (10.6k points)
...