Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d02862f
feat(mocks): add MockMethodCall<TReturn> and VoidMockMethodCall unifi…
thomhurst Feb 23, 2026
88d5d29
feat(mocks): add PropertyMockCall<TProperty> and PropertySetterMockCa…
thomhurst Feb 23, 2026
9e29594
feat(mocks): simplify Mock<T> by removing Setup/Verify/Raise surfaces
thomhurst Feb 23, 2026
4ac0258
feat(mocks): replace MockSetupBuilder + MockVerifyBuilder with unifie…
thomhurst Feb 23, 2026
05814e9
refactor: update RaiseBuilder and factory builders for simplified Moc…
thomhurst Feb 23, 2026
d0a70f0
refactor(mocks): update all test files for unified Mock<T> API
thomhurst Feb 23, 2026
ff689f0
fix(mocks): eagerly register void method and property setter setups
thomhurst Feb 23, 2026
c41427e
docs: update mocking documentation for unified Mock<T> API
thomhurst Feb 23, 2026
f225a2c
fix(mocks): use Lazy<T> for thread-safe setup registration in non-voi…
thomhurst Feb 23, 2026
f5f06f4
fix(mocks): address review feedback — member names, setter registrati…
thomhurst Feb 23, 2026
78c57a3
fix(mocks): use Lazy<T> in PropertySetterMockCall for consistency
thomhurst Feb 23, 2026
c6690cc
fix(mocks): cache matchers/name in PropertySetterMockCall, clarify ge…
thomhurst Feb 23, 2026
7db470f
feat(mocks): generate strongly-typed SetsOut/SetsRef methods for out/…
thomhurst Feb 23, 2026
9346b08
fix(mocks): address review feedback — attribute ordering, modern syntax
thomhurst Feb 23, 2026
f7495b3
fix(mocks): eliminate PropertySetterMockCall duplication, consolidate…
thomhurst Feb 23, 2026
42b8448
fix(mocks): document eager registration rationale on PropertySetterMo…
thomhurst Feb 23, 2026
b603826
fix(mocks): use Lazy<T> in generated void wrapper classes for thread …
thomhurst Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs: update mocking documentation for unified Mock<T> API
Remove all references to the old .Setup., .Verify., and .Raise.
intermediate properties. Update all code examples to use the new
unified API where methods are called directly on Mock<T> and the
chain method disambiguates between setup and verification. Also
update XML doc comments in PropertyMockCall and MockAssertionExtensions
to reference current type names.
  • Loading branch information
thomhurst committed Feb 23, 2026
commit c41427eccc8d90aefec793bdb620efcb282d9e69
6 changes: 3 additions & 3 deletions TUnit.Mocks.Assertions/MockAssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace TUnit.Mocks.Assertions;

/// <summary>
/// Extension methods for asserting mock call verification through the TUnit assertion pipeline.
/// Enables: <c>await Assert.That(mock.Verify.Method()).WasCalled(Times.Once);</c>
/// Enables: <c>await Assert.That(mock.Method()).WasCalled(Times.Once);</c>
/// </summary>
public static class MockAssertionExtensions
{
Expand All @@ -24,7 +24,7 @@ public static WasCalledAssertion WasCalled(

/// <summary>
/// Asserts that the mock member was called the specified number of times.
/// Generic overload for types implementing <see cref="ICallVerification"/> (e.g. PropertyVerifyAccessor).
/// Generic overload for types implementing <see cref="ICallVerification"/> (e.g. PropertyMockCall).
/// </summary>
public static WasCalledAssertion WasCalled<T>(
this IAssertionSource<T> source,
Expand All @@ -48,7 +48,7 @@ public static WasNeverCalledAssertion WasNeverCalled(

/// <summary>
/// Asserts that the mock member was never called.
/// Generic overload for types implementing <see cref="ICallVerification"/> (e.g. PropertyVerifyAccessor).
/// Generic overload for types implementing <see cref="ICallVerification"/> (e.g. PropertyMockCall).
/// </summary>
public static WasNeverCalledAssertion WasNeverCalled<T>(
this IAssertionSource<T> source) where T : ICallVerification
Expand Down
4 changes: 2 additions & 2 deletions TUnit.Mocks/PropertyMockCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace TUnit.Mocks;

/// <summary>
/// Unified return type for property mock members. Supports both setup and verification.
/// Merges what was previously split between <c>PropertySetupAccessor&lt;TProperty&gt;</c>
/// and <c>PropertyVerifyAccessor&lt;TProperty&gt;</c>.
/// Provides setup (via <c>.Returns()</c>, <c>.Getter</c>, <c>.Setter</c>, <c>.Set()</c>)
/// and verification (via <c>.WasCalled()</c>, <c>.WasNeverCalled()</c>) on a single type.
/// Public for generated code access. Not intended for direct use.
/// </summary>
/// <typeparam name="TProperty">The property type.</typeparam>
Expand Down
30 changes: 15 additions & 15 deletions docs/docs/test-authoring/mocking/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sidebar_position: 5

### Raising Events

If the mocked interface declares events, TUnit.Mocks generates a `Raise` surface:
If the mocked interface declares events, TUnit.Mocks generates `Raise{EventName}()` extension methods directly on `Mock<T>`:

```csharp
public interface IConnection
Expand All @@ -24,7 +24,7 @@ string? received = null;
mock.Object.OnMessage += (sender, msg) => received = msg;

// Raise the event through the mock
mock.Raise.OnMessage("Hello!");
mock.RaiseOnMessage("Hello!");
// received == "Hello!"
```

Expand All @@ -33,7 +33,7 @@ mock.Raise.OnMessage("Hello!");
Trigger an event automatically when a method is called using the typed `.Raises{EventName}()` method on a setup chain:

```csharp
mock.Setup.SendMessage(Arg.Any<string>())
mock.SendMessage(Arg.Any<string>())
.RaisesOnMessage("echo");

mock.Object.SendMessage("test");
Expand Down Expand Up @@ -76,16 +76,16 @@ public interface IConnection
var mock = Mock.Of<IConnection>();
mock.SetState("disconnected");

mock.InState("disconnected", setup =>
mock.InState("disconnected", m =>
{
setup.GetStatus().Returns("OFFLINE");
setup.Connect().TransitionsTo("connected");
m.GetStatus().Returns("OFFLINE");
m.Connect().TransitionsTo("connected");
});

mock.InState("connected", setup =>
mock.InState("connected", m =>
{
setup.GetStatus().Returns("ONLINE");
setup.Disconnect().TransitionsTo("disconnected");
m.GetStatus().Returns("ONLINE");
m.Disconnect().TransitionsTo("disconnected");
});

// Start disconnected
Expand Down Expand Up @@ -130,7 +130,7 @@ var serviceB = mock.Object.GetServiceB();

// Configure the auto-mock
var autoMock = mock.GetAutoMock<IServiceB>("GetServiceB");
autoMock.Setup.GetValue().Returns(42);
autoMock.GetValue().Returns(42);

var value = serviceB.GetValue(); // 42
```
Expand All @@ -148,8 +148,8 @@ var serviceMock = repo.Of<IService>();
var loggerMock = repo.Of<ILogger>();

// Configure each mock individually
serviceMock.Setup.GetData(Arg.Any<int>()).Returns("result");
loggerMock.Setup.Log(Arg.Any<string>());
serviceMock.GetData(Arg.Any<int>()).Returns("result");
loggerMock.Log(Arg.Any<string>());

// Exercise code
serviceMock.Object.GetData(1);
Expand Down Expand Up @@ -181,8 +181,8 @@ repo.Reset(); // clear all mocks
Get a diagnostic report of setup coverage and call matching:

```csharp
mock.Setup.GetUser(Arg.Any<int>()).Returns(new User("Alice"));
mock.Setup.Delete(Arg.Any<int>());
mock.GetUser(Arg.Any<int>()).Returns(new User("Alice"));
mock.Delete(Arg.Any<int>());

svc.GetUser(1);
// Delete was never called
Expand Down Expand Up @@ -228,7 +228,7 @@ The provider is consulted **before** auto-mocking and built-in smart defaults.
Clear all setups, call history, state, and auto-tracked property values:

```csharp
mock.Setup.GetUser(Arg.Any<int>()).Returns(new User("Alice"));
mock.GetUser(Arg.Any<int>()).Returns(new User("Alice"));
svc.GetUser(1);

mock.Reset();
Expand Down
44 changes: 22 additions & 22 deletions docs/docs/test-authoring/mocking/argument-matchers.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sidebar_position: 4

# Argument Matchers

Argument matchers control which calls a setup or verification matches. They are used in both `mock.Setup` and `mock.Verify` surfaces.
Argument matchers control which calls a setup or verification matches. The same matchers work in both contexts — the chain method determines whether it's a setup or verification.

## Quick Reference

Expand Down Expand Up @@ -33,7 +33,7 @@ Argument matchers control which calls a setup or verification matches. They are
Matches any value, including null:

```csharp
mock.Setup.GetUser(Arg.Any<int>()).Returns(new User("Default"));
mock.GetUser(Arg.Any<int>()).Returns(new User("Default"));

svc.GetUser(1); // matches
svc.GetUser(999); // matches
Expand All @@ -44,8 +44,8 @@ svc.GetUser(999); // matches
Match a specific value using equality:

```csharp
mock.Setup.GetUser(Arg.Is(42)).Returns(new User("Alice"));
mock.Setup.GetUser(Arg.Is(99)).Returns(new User("Bob"));
mock.GetUser(Arg.Is(42)).Returns(new User("Alice"));
mock.GetUser(Arg.Is(99)).Returns(new User("Bob"));

svc.GetUser(42); // returns Alice
svc.GetUser(99); // returns Bob
Expand All @@ -55,8 +55,8 @@ svc.GetUser(1); // no match — returns default
:::tip Implicit Conversion
Raw values are implicitly converted to `Arg.Is(value)`, so these are equivalent:
```csharp
mock.Setup.GetUser(Arg.Is(42)).Returns(new User("Alice"));
mock.Setup.GetUser(42).Returns(new User("Alice")); // same thing
mock.GetUser(Arg.Is(42)).Returns(new User("Alice"));
mock.GetUser(42).Returns(new User("Alice")); // same thing
```
:::

Expand All @@ -65,15 +65,15 @@ mock.Setup.GetUser(42).Returns(new User("Alice")); // same thing
Match values satisfying a condition:

```csharp
mock.Setup.GetUser(Arg.Is<int>(id => id > 0)).Returns(new User("Valid"));
mock.Setup.GetUser(Arg.Is<int>(id => id <= 0)).Throws<ArgumentException>();
mock.GetUser(Arg.Is<int>(id => id > 0)).Returns(new User("Valid"));
mock.GetUser(Arg.Is<int>(id => id <= 0)).Throws<ArgumentException>();
```

### Null and NotNull

```csharp
mock.Setup.Process(Arg.IsNull<string>()).Returns("was null");
mock.Setup.Process(Arg.IsNotNull<string>()).Returns("had value");
mock.Process(Arg.IsNull<string>()).Returns("was null");
mock.Process(Arg.IsNotNull<string>()).Returns("had value");
```

## String Matchers
Expand All @@ -82,7 +82,7 @@ mock.Setup.Process(Arg.IsNotNull<string>()).Returns("had value");

```csharp
// Match strings against a regex pattern
mock.Setup.Search(Arg.Matches(@"^user_\d+$")).Returns(new[] { "found" });
mock.Search(Arg.Matches(@"^user_\d+$")).Returns(new[] { "found" });

svc.Search("user_42"); // matches
svc.Search("admin_1"); // no match
Expand All @@ -92,15 +92,15 @@ svc.Search("admin_1"); // no match

```csharp
var pattern = new Regex(@"^user_\d+$", RegexOptions.Compiled);
mock.Setup.Search(Arg.Matches(pattern)).Returns(new[] { "found" });
mock.Search(Arg.Matches(pattern)).Returns(new[] { "found" });
```

## Collection Matchers

### Contains

```csharp
mock.Setup.ProcessItems(Arg.Contains<List<int>, int>(42)).Returns(true);
mock.ProcessItems(Arg.Contains<List<int>, int>(42)).Returns(true);

svc.ProcessItems(new List<int> { 1, 42, 3 }); // matches — contains 42
svc.ProcessItems(new List<int> { 1, 2, 3 }); // no match
Expand All @@ -109,7 +109,7 @@ svc.ProcessItems(new List<int> { 1, 2, 3 }); // no match
### HasCount

```csharp
mock.Setup.ProcessItems(Arg.HasCount<List<int>>(3)).Returns(true);
mock.ProcessItems(Arg.HasCount<List<int>>(3)).Returns(true);

svc.ProcessItems(new List<int> { 1, 2, 3 }); // matches — count is 3
svc.ProcessItems(new List<int> { 1, 2 }); // no match
Expand All @@ -118,7 +118,7 @@ svc.ProcessItems(new List<int> { 1, 2 }); // no match
### IsEmpty

```csharp
mock.Setup.ProcessItems(Arg.IsEmpty<List<int>>()).Returns(false);
mock.ProcessItems(Arg.IsEmpty<List<int>>()).Returns(false);

svc.ProcessItems(new List<int>()); // matches
svc.ProcessItems(new List<int> { 1 }); // no match
Expand All @@ -127,7 +127,7 @@ svc.ProcessItems(new List<int> { 1 }); // no match
### SequenceEquals

```csharp
mock.Setup.ProcessItems(
mock.ProcessItems(
Arg.SequenceEquals<List<int>, int>(new[] { 1, 2, 3 })
).Returns(true);

Expand All @@ -140,7 +140,7 @@ svc.ProcessItems(new List<int> { 3, 2, 1 }); // no match — wrong order
### IsInRange

```csharp
mock.Setup.SetAge(Arg.IsInRange(0, 120)).Returns(true);
mock.SetAge(Arg.IsInRange(0, 120)).Returns(true);

svc.SetAge(25); // matches
svc.SetAge(-1); // no match
Expand All @@ -150,16 +150,16 @@ svc.SetAge(200); // no match
### IsIn / IsNotIn

```csharp
mock.Setup.GetRole(Arg.IsIn("admin", "editor", "viewer")).Returns(true);
mock.Setup.GetRole(Arg.IsNotIn("admin", "superadmin")).Returns(false);
mock.GetRole(Arg.IsIn("admin", "editor", "viewer")).Returns(true);
mock.GetRole(Arg.IsNotIn("admin", "superadmin")).Returns(false);
```

## Negation

Wrap any matcher with `Not` to invert it:

```csharp
mock.Setup.Process(Arg.Not(Arg.Is(0))).Returns("non-zero");
mock.Process(Arg.Not(Arg.Is(0))).Returns("non-zero");
// Matches any int except 0
```

Expand All @@ -169,7 +169,7 @@ Every `Arg<T>` matcher automatically captures the values it sees:

```csharp
var nameArg = Arg.Any<string>();
mock.Setup.Greet(nameArg).Returns("Hi");
mock.Greet(nameArg).Returns("Hi");

svc.Greet("Alice");
svc.Greet("Bob");
Expand Down Expand Up @@ -213,7 +213,7 @@ public class StringLengthMatcher : IArgumentMatcher<string>
}

// Usage
mock.Setup.Greet(Arg.Matches(new StringLengthMatcher(3, 50)))
mock.Greet(Arg.Matches(new StringLengthMatcher(3, 50)))
.Returns("Valid name");
```

Expand Down
14 changes: 7 additions & 7 deletions docs/docs/test-authoring/mocking/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ public class GreeterTests
var mock = Mock.Of<IGreeter>();

// Configure — set up a return value
mock.Setup.Greet(Arg.Any<string>()).Returns("Hello!");
mock.Greet(Arg.Any<string>()).Returns("Hello!");

// Act — use the mock object
IGreeter greeter = mock.Object;
var result = greeter.Greet("Alice");

// Assert — verify the result and the call
await Assert.That(result).IsEqualTo("Hello!");
mock.Verify.Greet("Alice").WasCalled(Times.Once);
mock.Greet("Alice").WasCalled(Times.Once);
}
}
```
Expand Down Expand Up @@ -86,15 +86,15 @@ var strict = Mock.Of<IService>(MockBehavior.Strict); // throws on unconfigured

### The Mock Wrapper

`Mock.Of<T>()` returns a `Mock<T>` wrapper with three surfaces:
`Mock.Of<T>()` returns a `Mock<T>` wrapper. Extension methods are generated directly on `Mock<T>` for each member of the mocked type, and the chain methods (`.Returns()`, `.WasCalled()`, etc.) disambiguate between setup and verification:

```csharp
var mock = Mock.Of<IService>();

mock.Setup // configure method/property behaviors
mock.Verify // verify calls were made
mock.Raise // raise events (if T has events)
mock.Object // the T instance to pass to your code under test
mock.GetUser(Arg.Any<int>()).Returns(user); // setup — .Returns() makes it a stub
mock.GetUser(42).WasCalled(Times.Once); // verify — .WasCalled() makes it a check
mock.RaiseOnMessage("hi"); // raise events — Raise{EventName}()
mock.Object // the T instance to pass to your code under test
```

### Implicit Conversion
Expand Down
Loading
Loading