Overview
This article explains how to define the behavior of interface properties using the Moq library for .NET unit testing. We will cover two patterns: “read-only” behavior that returns a fixed value, and “auto-implemented” behavior that tracks and remembers assigned values. You can choose the best method depending on whether your test code only reads the property or needs to update and reuse its value.
Specifications (Input/Output)
- Input:
- The interface definition you want to mock.
SetupGet: A fixed value to return when the property is accessed.SetupProperty: An optional initial value for a property that tracks changes.
- Output:
- With
SetupGet: The property always returns the specified fixed value. Even if asetoperation is performed, the value remains unchanged. - With
SetupProperty: The property behaves like a standard class property. It retains values assigned viasetand returns them viaget.
- With
- Prerequisite: The NuGet package
Moqmust be installed.
Basic Usage
This is the basic form used to ensure a specific property always returns a fixed value when accessed.
// Create the mock
var mock = new Mock<IUserSettings>();
// Configure the Theme property to always return "Dark"
mock.SetupGet(x => x.Theme).Returns("Dark");
// Retrieval
var value = mock.Object.Theme; // "Dark"
Full Code
The following console application compares the “fixed value” approach with the “change tracking” approach.
using System;
using Moq;
// Requires Moq package installation via NuGet:
// dotnet add package Moq
namespace MoqPropertyExample
{
// Interface to be mocked
public interface IServerConfiguration
{
string HostName { get; set; }
int TimeoutSeconds { get; set; }
}
class Program
{
static void Main()
{
Console.WriteLine("--- 1. Fixed Return Value (SetupGet) ---");
RunSetupGetDemo();
Console.WriteLine("\n--- 2. Tracking Value Changes (SetupProperty) ---");
RunSetupPropertyDemo();
}
static void RunSetupGetDemo()
{
var mock = new Mock<IServerConfiguration>();
// When the HostName property is accessed, it always returns "db-server-01"
// Note: Setting a value will be ignored; the Get result will not change.
mock.SetupGet(m => m.HostName).Returns("db-server-01");
var config = mock.Object;
Console.WriteLine($"Initial Value: {config.HostName}");
// Attempting to change the value will not be reflected because it is fixed by SetupGet.
config.HostName = "web-server-99";
Console.WriteLine($"After Change: {config.HostName}");
}
static void RunSetupPropertyDemo()
{
var mock = new Mock<IServerConfiguration>();
// Make the TimeoutSeconds property act like a normal property (tracking values)
// You can set an initial value in the second argument (30 in this case)
mock.SetupProperty(m => m.TimeoutSeconds, 30);
var config = mock.Object;
Console.WriteLine($"Initial Value: {config.TimeoutSeconds}");
// Changing the value updates the internal state of the mock object
config.TimeoutSeconds = 60;
Console.WriteLine($"After Change: {config.TimeoutSeconds}");
// Change it again
config.TimeoutSeconds = 120;
Console.WriteLine($"Second Change: {config.TimeoutSeconds}");
}
}
}
Output Example
--- 1. Fixed Return Value (SetupGet) ---
Initial Value: db-server-01
After Change: db-server-01
--- 2. Tracking Value Changes (SetupProperty) ---
Initial Value: 30
After Change: 60
Second Change: 120
Customization Points
- Dynamic Return Values: Using
Returns(() => SomeFunction())instead ofReturns("FixedValue")allows you to calculate and return a value every time the property is accessed. - Initial Value Settings: The second argument of
SetupPropertyis optional. If omitted, the property is initialized with the default value of its type (such asnullor0).
Points of Caution
- Priority of SetupGet: If you
seta value to a property configured withSetupGet, no exception is thrown. However, the nextgetcall will still return the value defined inSetupGet. This behavior can be confusing if your test assumes the value will update. - Hierarchical Properties: When mocking deep hierarchies like
mock.Setup(m => m.Child.Property), ensure that the intermediateChildproperty does not returnnullby properly setting up the parent mock. - Interface Constraints: You cannot use
SetupPropertyon properties that do not have asetaccessor in the interface definition.
Application
Automatically Implementing All Properties
If an interface has many properties and you want all of them to track values, writing SetupProperty for each one is tedious. You can use SetupAllProperties to configure them all at once.
var mock = new Mock<IServerConfiguration>();
// Enable all properties in the interface to track values
mock.SetupAllProperties();
// You can still set specific initial values manually
mock.Object.HostName = "localhost";
mock.Object.TimeoutSeconds = 500;
This method is very effective when you need a mock that acts as a simple Data Transfer Object (DTO).
Summary
For bulk configurations, SetupAllProperties() is a convenient way to make all properties auto-implemented. If you only need a property to return a constant value for reading purposes, SetupGet(m => m.Prop).Returns(value) is the appropriate choice. When you need an object that maintains its state throughout a test as values are updated, use SetupProperty(m => m.Prop, initialValue).
