Skip to main content

Advanced callback features

Conditional callbacks (When)​

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

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

Limit invocations (Only)​

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

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

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

Repeat invocations (For)​

Control how many times a callback should be repeated:

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

sut.Mock.Setup.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:

sut.Mock.Setup.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​

When you specify multiple callbacks, they are executed sequentially by default. You can change this behavior to always run specific callbacks in parallel using .InParallel():

sut.Mock.Setup.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do(() => { Console.WriteLine("Runs every second iteration"); })
.Do(() => { Console.WriteLine("Runs always in parallel"); }).InParallel()
.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:

sut.Mock.Setup.Dispense(It.IsAny<string>(), It.IsAny<int>())
.Do((count, _, _) => Console.WriteLine($"Call #{count}"));

sut.Mock.Setup.TotalDispensed.OnGet
.Do((count, value) => Console.WriteLine($"Read #{count}, value: {value}"));

// Indexer setter - count, then the indexer key(s), then the new value
sut.Mock.Setup[It.IsAny<string>()].OnSet
.Do((count, type, newValue) =>
Console.WriteLine($"Set #{count}: this[{type}] = {newValue}"));