Skip to content

Commit 840405e

Browse files
Merge pull request #3 from JeanMarcMbouma/copilot/add-strongly-typed-setup-oncall
2 parents a7d234f + f21a245 commit 840405e

File tree

3 files changed

+1157
-4
lines changed

3 files changed

+1157
-4
lines changed

README.md

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ A lightweight, high-performance mocking framework for .NET that combines compile
1212
- 🎯 **Argument Matching** - Pattern matching for mock setup and verification
1313
- 🔍 **Verification** - Flexible verification with Times predicates (`Once`, `Never`, `Exactly`, `AtLeast`, `AtMost`)
1414
- 🔗 **Callbacks** - Execute custom logic when methods are called or properties are accessed
15+
- 💎 **Strongly-Typed Handlers** - Type-safe Setup and OnCall with partial parameter signatures
1516
- 🎓 **Easy API** - Simple, intuitive API inspired by popular mocking frameworks
1617

1718
## Getting Started
@@ -98,6 +99,9 @@ foreach (var invocation in builder.Invocations)
9899

99100
**Setup Methods:**
100101
- `Setup<TResult>(expression, behavior)` - Configure method return values
102+
- `Setup<TResult, T1>(expression, handler)` - Configure with strongly-typed handler receiving first parameter
103+
- `Setup<TResult, T1, T2>(expression, handler)` - Configure with handler receiving first two parameters
104+
- `Setup<TResult, T1, T2, T3>(expression, handler)` - Configure with handler receiving first three parameters
101105
- `SetupGet<TProp>(property, getter)` - Setup property getter behavior
102106
- `ReturnsGet<TProp>(property, value)` - Convenience method for constant property values
103107
- `SetupSet<TProp>(property, setter)` - Setup property setter behavior
@@ -111,6 +115,10 @@ foreach (var invocation in builder.Invocations)
111115

112116
**Callback Methods:**
113117
- `OnCall(expression, callback)` - Execute logic when method is called
118+
- `OnCall(expression, handler)` - Execute logic with no parameters when method is called
119+
- `OnCall<T1>(expression, handler)` - Execute logic with strongly-typed first parameter
120+
- `OnCall<T1, T2>(expression, handler)` - Execute logic with first two parameters
121+
- `OnCall<T1, T2, T3>(expression, handler)` - Execute logic with first three parameters
114122
- `OnCall(expression, matcher, callback)` - Execute logic when method is called with matching arguments
115123
- `OnPropertyAccess<T>(property, callback)` - Execute logic on property get or set
116124
- `OnGetCallback<T>(property, callback)` - Execute logic when property getter is accessed
@@ -121,21 +129,90 @@ foreach (var invocation in builder.Invocations)
121129
- `Object` - Get the mock instance
122130
- `Invocations` - Access all recorded invocations for custom verification
123131

132+
## Strongly-Typed Callbacks with Partial Parameters
133+
134+
BbQ.MockLite allows you to configure callbacks and return value handlers that receive only the parameters you care about, while maintaining full type safety.
135+
136+
### Setup with Strongly-Typed Handlers
137+
138+
Configure method return values using handlers that receive a subset of the method's parameters:
139+
140+
```csharp
141+
using BbQ.MockLite;
142+
143+
var builder = Mock.Create<IQueryService>();
144+
145+
// Handler receives only the first parameter (type-safe)
146+
builder.Setup(x => x.Query("proc", 1, 2),
147+
(string proc) => $"Result: {proc}");
148+
149+
// Handler receives first two parameters
150+
builder.Setup(x => x.Query("proc", 1, 2),
151+
(string proc, int id) => $"{proc}-{id}");
152+
153+
// Handler receives all three parameters
154+
builder.Setup(x => x.Query("proc", 1, 2),
155+
(string proc, int id, int count) => $"{proc}:{id}:{count}");
156+
157+
// Parameterless handler (doesn't need any parameters)
158+
builder.Setup(x => x.Query("proc", 1, 2),
159+
() => "Fixed result");
160+
```
161+
162+
### OnCall with Strongly-Typed Handlers
163+
164+
Execute custom logic using strongly-typed handlers that receive only the parameters you need:
165+
166+
```csharp
167+
using BbQ.MockLite;
168+
169+
var builder = Mock.Create<IUserRepository>();
170+
171+
// Parameterless callback
172+
builder.OnCall(x => x.GetUser(It.IsAny<string>()),
173+
() => Console.WriteLine("GetUser called"));
174+
175+
// Type-safe callback with first parameter
176+
builder.OnCall(x => x.GetUser(It.IsAny<string>()),
177+
(string userId) => Console.WriteLine($"Getting user: {userId}"));
178+
179+
// Type-safe callback with multiple parameters
180+
builder.OnCall(x => x.UpdateUser(It.IsAny<string>(), It.IsAny<User>(), It.IsAny<bool>()),
181+
(string userId, User user) =>
182+
Console.WriteLine($"Updating {userId} to {user.Name}"));
183+
184+
// Works with void methods too
185+
builder.OnCall(x => x.DeleteUser(It.IsAny<string>(), It.IsAny<bool>()),
186+
(string userId) => Console.WriteLine($"Deleting: {userId}"));
187+
```
188+
189+
### Benefits
190+
191+
- **Type Safety**: Compile-time checking ensures parameter types match
192+
- **Clean Code**: Only handle the parameters you care about, ignore the rest
193+
- **IntelliSense Support**: Full IDE support with parameter names and types
194+
- **Flexible**: Works with methods that have any number of parameters
195+
124196
## Callbacks for Custom Logic Execution
125197

126198
BbQ.MockLite supports callbacks that execute custom logic when methods are called or properties are accessed. This is useful for audit logging, state management, and complex verification scenarios.
127199

128200
```csharp
129201
using BbQ.MockLite;
130202

131-
// Track method calls with custom logic
203+
// Track method calls with custom logic using strongly-typed handlers
132204
var auditLog = new List<string>();
133205
var builder = Mock.Create<IUserRepository>()
206+
// Traditional approach with object array
134207
.OnCall(x => x.GetUser(It.IsAny<string>()),
135208
args => auditLog.Add($"GetUser called with: {args[0]}"))
209+
// Strongly-typed approach (new)
136210
.OnCall(x => x.SaveUser(It.IsAny<User>()),
137-
args => args[0] is User u && u.IsAdmin,
138-
args => auditLog.Add($"Admin user saved: {((User)args[0]).Name}"));
211+
(User user) => auditLog.Add($"SaveUser called with: {user.Name}"))
212+
// Conditional callback with matcher
213+
.OnCall(x => x.DeleteUser(It.IsAny<string>()),
214+
args => args[0] is string id && id.StartsWith("admin"),
215+
args => auditLog.Add($"Admin user deleted: {args[0]}"));
139216

140217
var mock = builder.Object;
141218

@@ -152,13 +229,23 @@ Assert.Equal(2, auditLog.Count);
152229
**Track method calls:**
153230
```csharp
154231
var callCount = 0;
232+
// Parameterless callback
233+
mock.OnCall(x => x.Process(It.IsAny<string>()),
234+
() => callCount++);
235+
```
236+
237+
**Type-safe parameter access:**
238+
```csharp
239+
var processedItems = new List<string>();
240+
// Strongly-typed callback
155241
mock.OnCall(x => x.Process(It.IsAny<string>()),
156-
args => callCount++);
242+
(string item) => processedItems.Add(item));
157243
```
158244

159245
**Conditional callbacks:**
160246
```csharp
161247
var adminActions = new List<string>();
248+
// Mix of type-safe handlers and matchers
162249
mock.OnCall(
163250
x => x.Execute(It.IsAny<string>()),
164251
args => args[0] is string s && s.StartsWith("admin"),

0 commit comments

Comments
 (0)