Hi peterf,
There are two things resulting this behavior, the first one is the fact the test is placing an expectation on a test class property and the second one is the default faking behavior when a chain of methods (e.g.
a().b().c()) is faked in a single when called.
Expectation on test class property
The main difference between the two examples, first with field and second with property, is that a property getter is actually a method call by itself. When the getter appears inside the
WhenCalled, it is actually referring to the method (e.g. get_fakeProperty()) and not the value stored in it before.
For example:
[TestFixture]
public class TestWithProperty
{
private TestClass fakeProperty { get; set; }
[Test]
[Isolated]
public void VariableTest()
{
fakeProperty = null;
Isolate.WhenCalled(() => fakeProperty.GetDummyString()).WillReturn("Test");
// Same as:
//Isolate.WhenCalled(() => this.get_fakeProperty().GetDummyString()).WillReturn("Test");
Assert.AreEqual("Test", fakeProperty.GetDummyString());
}
}
What we see in this example is that the actual faked behavior is defined on the getter method and not on the value which was sent to the setter method before that (in our example, a null value). This feature allows tests to define faked behavior on real instances. This is known as
live object. In the original example, the actual instance which was faked (just a single method) was the test class itself, on which the property is defined (
this.get_fakeProperty()).
Default faking behavior when a chain of methods (e.g. a().b().c()) is faked
When faking multiple methods in a single
WhenCalled (aka chain) all the instances along the path are faked. The default fake behavior depends on the originating fake. For example, if we have
fakeA which is a recursive fake, a method called on it, like
GetB() will also return a recursive fake. But, if the originating instance is a fake instantiated with
CallOriginal or instead a live object, then the fakes defined along the path are also
CallOriginal fakes. The difference in this case, is that when the fake is created, it's constructor will be ignored. This is similar to creating a fake with
Isolate.Fake.Instance<TestClass>(Members.CallOriginal, ConstructorWillBe.Ignored).
In the original example, what we got was an instance that called the actual method (
Test) but since the constructor was not executed, the value of the
TestString field was not initialized.
Solution
Assuming the intention was to set a behavior on the fake instance it's possible to do one of the next:
1. Avoid a usage of property in the test class, instead use fields.
2. Before calling
WhenCalled, extract the property value into a local variable and use the variable inside the [i]WhenCalled[i/]
For example:
fakeProperty = Isolate.Fake.Instance<TestClass>(Members.CallOriginal);
var fake = fakeProperty;
Isolate.WhenCalled(() => fake.GetDummyString()).WillReturn("Test");
Please let us know if it's clear.