Reputation: 661
In my test project, I want to capture a property set by the SUT to a mocked object. I have tried many things, but none seem to allow me to capture that.
I set up a short example:
The mocked interface:
public interface ISomeInterface
{
string SomeProperty { get; set; }
}
The SUT:
public class SomeSystemUnderTest
{
public void AssignSomeValueToThis(ISomeInterface obj)
{
obj.SomeProperty = Guid.NewGuid().ToString();
}
}
The test:
[TestClass]
public class SomeTests
{
[TestMethod]
public void TestSomeSystem()
{
// Arrange
var someInterfaceMock = new Mock<ISomeInterface>();
someInterfaceMock.SetupSet(m => m.SomeProperty = It.IsAny<string>()).Verifiable();
// Act
var sut = new SomeSystemUnderTest();
sut.AssignSomeValueToThis(someInterfaceMock.Object);
// Assert
// HERE I WOULD LIKE TO READ WHAT VALUE WAS ASSIGNED
string myVal = someInterfaceMock.Object.SomeProperty;
}
}
The "myVal" variable stays null, and by examining the mock, we can see the property is still null. I did not really expected it to have some value, just trying.
I tried with Setup, with a callback, I get compilation errors.
In real life project, the SUT is to transform a mocked object property to something dependant to another object property. To know if the object is doing its job, I need to be able to read the property. Note, I cannot re-designed the mocked interfaces, they are 3rd party.
I tried to use VerifySet, but it seems to take a hard-coded value only.
Thank you, Michel
Upvotes: 6
Views: 7154
Reputation: 12711
General Answer
Moq actually has special methods for that:
mock.SetupProperty(m => m.SomeProperty)
, optionally passing a default valuemock.SetupAllProperties()
Specific Cases
There are however specific cases where this isn't enough, for example if we want to setup a property and give it a default value without setting the property value via the property setters.
(One example case, is when we need the property with the set value for use in the constructor, in which case we cannot set the property directly as it would require calling .Object
which implicitly calls the constructor).
In this situation one should use the answer of @Johnny.
Here is a generic extension method version of his answer:
public static void SetupAutoProperty<TObject, TProperty>(this Mock<TObject> mock,
Expression<Func<TObject, TProperty>> memberAccessExpr,
TProperty initialValue) where TObject : class
{
var propStates = new List<TProperty>();
Expression<Action> captureExpression = () => Capture.In(propStates);
var finalExpression = Expression.Lambda<Action<TObject>>(
Expression.Assign(memberAccessExpr.Body, captureExpression.Body),
memberAccessExpr.Parameters);
mock.SetupSet(finalExpression.Compile());
mock.SetupGet(memberAccessExpr).Returns(() =>
propStates.Any() ? propStates.Last() : initialValue);
}
And call it as in someInterfaceMock.SetupAutoProperty(m => m.SomeProperty, someInitialValue)
Upvotes: 3
Reputation: 9509
There is a difference between get
and set
and mock actually doesn't have any internal state but only the setups which it tries to match and behave properly. You could mimic real get
and set
functionality by using callback. Something like this:
//Arrange
string someProperty = null;
var mock = new Mock<ISomeInterface>();
mock.SetupSet(m => m.SomeProperty = It.IsAny<string>())
.Callback<string>(p => someProperty = p)
.Verifiable();
// use func instead of value to defer the resulution to the invocation moment
mock.SetupGet(m => m.SomeProperty).Returns(() => someProperty);
//Act
mock.Object.SomeProperty = "test";
//Assert
Assert.AreEqual("test", mock.Object.SomeProperty);
The other possibility is to use Capture
itself it actually exist within moq
//Arrange
List<string> someProperty = new List<string>();
var mock = new Mock<ISomeInterface>();
mock.SetupSet(m => m.SomeProperty = Capture.In(someProperty))
.Verifiable();
mock.SetupGet(m => m.SomeProperty).Returns(() => someProperty.Last());
//Act
mock.Object.SomeProperty = "test";
//Assert
Assert.AreEqual("test", mock.Object.SomeProperty);
Upvotes: 10