Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 37 additions & 9 deletions Docs/pages/advanced-features/02-advanced-callback-features.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
# Advanced callback features

## Conditional callbacks
## Conditional callbacks (`When`)

Execute callbacks conditionally based on the zero-based invocation counter using `.When()`:

```csharp
sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny<int>())
.Do(() => Console.WriteLine("Called!"))
.When(count => count >= 2); // The first two calls are skipped
.Do(() => Console.WriteLine("Called!")).When(count => count >= 2); // The first two calls are skipped
```

## Frequency control
## Limit invocations (`Only`)

Control how many times a callback executes:
Control after how many times a callback should no longer be executed:

```csharp
// Execute up to 3 times
sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => Console.WriteLine("Up to 3 times"))
.Only(3);
.Do(() => Console.WriteLine("Up to 3 times")).Only(3);

// Executes the callback only once
sut.SetupMock.Property.TotalDispensed
.Throws(new Exception("This exception is thrown only once")).OnlyOnce();
```

## Repeat invocations (`For`)

Control how many times a callback should be repeated:

```csharp
sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => Console.WriteLine("Only once"))
.OnlyOnce();
.Do(() => Console.WriteLine("First three times")).For(3)
.Do(() => Console.WriteLine("Next three times")).For(3);

sut.SetupMock.Property.TotalDispensed
.Returns(10).For(1)
.Returns(20).For(2)
.Returns(30).For(3);
// Reads: 10, 20, 20, 30, 30, 30, 0, 0, 0, 0 …
```

### Repeat `Forever`

If you have a sequence of callbacks, you can mark the last one to repeat indefinitely using `.Forever()` to avoid
repeating the sequence from start:

```csharp
sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Returns(true).For(2) // Returns true the first two times
.Returns(false).Forever(); // Then always returns false
```

## Parallel callbacks
Expand All @@ -38,6 +62,10 @@ sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => { Console.WriteLine("Runs every other iteration"); });
```

**Note:**
Parallel execution via `.InParallel()` only applies to callbacks defined via `Do`, not to other setup callbacks like
`Returns` or `Throws`.

## Invocation counter

Access the zero-based invocation counter in callbacks:
Expand Down
46 changes: 37 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -819,30 +819,54 @@ sut.VerifyMock.SetProtectedIndexer(It.Is(0), It.Is(42)).Once();

### Advanced callback features

#### Conditional callbacks
#### Conditional callbacks (`When`)

Execute callbacks conditionally based on the zero-based invocation counter using `.When()`:

```csharp
sut.SetupMock.Method.Dispense(It.Is("Dark"), It.IsAny<int>())
.Do(() => Console.WriteLine("Called!"))
.When(count => count >= 2); // The first two calls are skipped
.Do(() => Console.WriteLine("Called!")).When(count => count >= 2); // The first two calls are skipped
```

#### Frequency control
#### Limit invocations (`Only`)

Control how many times a callback executes:
Control after how many times a callback should no longer be executed:

```csharp
// Execute up to 3 times
sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => Console.WriteLine("Up to 3 times"))
.Only(3);
.Do(() => Console.WriteLine("Up to 3 times")).Only(3);

// Executes the callback only once
sut.SetupMock.Property.TotalDispensed
.Throws(new Exception("This exception is thrown only once")).OnlyOnce();
```

#### Repeat invocations (`For`)

Control how many times a callback should be repeated:

```csharp
sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => Console.WriteLine("First three times")).For(3)
.Do(() => Console.WriteLine("Next three times")).For(3);

sut.SetupMock.Property.TotalDispensed
.Returns(10).For(1)
.Returns(20).For(2)
.Returns(30).For(3);
// Reads: 10, 20, 20, 30, 30, 30, 0, 0, 0, 0 …
```

**Repeat `Forever`**

If you have a sequence of callbacks, you can mark the last one to repeat indefinitely using `.Forever()` to avoid
repeating the sequence from start:

```csharp
sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => Console.WriteLine("Only once"))
.OnlyOnce();
.Returns(true).For(2) // Returns true the first two times
.Returns(false).Forever(); // Then always returns false
```

#### Parallel callbacks
Expand All @@ -857,6 +881,10 @@ sut.SetupMock.Method.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => { Console.WriteLine("Runs every other iteration"); });
```

**Note:**
Parallel execution via `.InParallel()` only applies to callbacks defined via `Do`, not to other setup callbacks like
`Returns` or `Throws`.

#### Invocation counter

Access the zero-based invocation counter in callbacks:
Expand Down
Loading