Properties
Set up property getters and setters to control or verify property access on your mocks.
Initialization
You can initialize properties so they work like normal properties (setter changes the value, getter returns the last set value):
sut.Mock.Setup.TotalDispensed.InitializeWith(42);
You can also register a setup without providing a value (useful when ThrowWhenNotSetup is enabled):
IChocolateDispenser sut = IChocolateDispenser.CreateMock(MockBehavior.Default.ThrowingWhenNotSetup());
// Register property without value - won't throw
sut.Mock.Setup.TotalDispensed.Register();
Returns / Throws
Set up properties with Returns and Throws (supports sequences):
sut.Mock.Setup.TotalDispensed
.Returns(1)
.Returns(2)
.Throws(new Exception("Error"))
.Returns(4);
You can also return a value based on the previous value:
sut.Mock.Setup.TotalDispensed
.Returns(current => current + 10); // Increment by 10 each read
Callbacks
Register callbacks on the setter or getter:
sut.Mock.Setup.TotalDispensed.OnGet
.Do(() => Console.WriteLine("TotalDispensed was read!"));
sut.Mock.Setup.TotalDispensed.OnSet
.Do(newValue => Console.WriteLine($"Changed to {newValue}!") );
Callbacks can also receive the current value:
// Getter with the current value
sut.Mock.Setup.TotalDispensed
.OnGet.Do(value =>
Console.WriteLine($"Read TotalDispensed current value: {value}"));
// Setter with the new value
sut.Mock.Setup.TotalDispensed
.OnSet.Do(newValue =>
Console.WriteLine($"Set TotalDispensed to {newValue}"));
Callbacks also support sequences, similar to Returns and Throws:
sut.Mock.Setup.TotalDispensed.OnGet
.Do(() => Console.WriteLine("Execute on all even read interactions"))
.Do(() => Console.WriteLine("Execute on all odd read interactions"));
Properties with only one accessor
The setup and verify surfaces only offer the accessors the mock actually intercepts.
Register() and SkippingBaseClass(…) stay available on both, but the accessor-specific members do
not: a get-only property has no OnSet, and a set-only property has neither OnGet nor the
Returns/Throws read-sequence nor InitializeWith, since there is no getter to read the value
back.
public interface IChocolateInventory
{
int RemainingBars { get; } // no setter
string LastCountedBy { set; } // no getter
}
IChocolateInventory sut = IChocolateInventory.CreateMock();
sut.Mock.Setup.RemainingBars.Returns(3);
sut.Mock.Setup.RemainingBars.Register();
sut.Mock.Setup.RemainingBars.OnSet… // does not compile
sut.Mock.Setup.RemainingBars.Returns(3).OnSet… // does not compile
sut.Mock.Setup.LastCountedBy.OnSet.Do(value => { });
sut.Mock.Setup.LastCountedBy.Register();
sut.Mock.Setup.LastCountedBy.Returns("Ada")… // does not compile
sut.Mock.Setup.LastCountedBy.InitializeWith("Ada")… // does not compile
sut.Mock.Setup.LastCountedBy.OnSet.Do(value => { }).OnGet… // does not compile
The verify facade likewise offers the intercepted accessor only:
_ = sut.RemainingBars;
sut.LastCountedBy = "Ada";
await That(sut.Mock.Verify.RemainingBars.Got()).Once();
await That(sut.Mock.Verify.LastCountedBy.Set("Ada")).Once();
sut.Mock.Verify.RemainingBars.Set(3)… // does not compile
sut.Mock.Verify.LastCountedBy.Got()… // does not compile
This also applies when the property declares an accessor the mock cannot see, such as
{ get; internal set; } on a type from an assembly that does not grant InternalsVisibleTo. Writes
never reach the mock in that case, so configuring or verifying one could only ever report zero
interactions. See Mockolate0002 for when such a type is mockable at all.
Both facades are fully restricted: the fluent builders returned by Returns, Throws, Do and
TransitionTo stay on the narrowed surface, so no amount of chaining reaches the accessor the mock
does not intercept.
Notes:
- Use
.SkippingBaseClass(…)to override the base class behavior for a specific property (only for class mocks). - All callbacks and return values support more advanced features like conditional execution, frequency control, parallel execution, and access to the invocation counter. See Advanced callback features for details.