Method Callbacks¶
Callbacks run after the arranged result (or after the default result in Implicit mode when no Returns is provided).
Target type used in examples:
Example
using Imposter.Abstractions;
[assembly: GenerateImposter(typeof(Imposter.Tests.Docs.Methods.ICallbackService))]
public interface ICallbackService
{
int GetNumber();
int Increment(int v);
int GenericAllRefKind(out int o, ref string r, in double d, bool[] args);
}
Ordering relative to results¶
Example
var stages = new List<string>();
imposter.GetNumber()
.Returns(() => { stages.Add("return"); return 42; })
.Callback(() => stages.Add("first"))
.Callback(() => stages.Add("second"));
service.GetNumber();
// stages == ["return", "first", "second"]
Parameterized callbacks¶
Capture input arguments in a callback:
Example
imposter.Increment(Arg<int>.Any()).Callback(v => Logger.Log($"value: {v}"));
Ref/out/in with callbacks¶
Callbacks can work with out, ref, and in parameters. Use OutArg<T>.Any() to match out parameters in the setup signature.
Example
imposter.GenericAllRefKind<int, string, double, bool, int>(
OutArg<int>.Any(),
Arg<string>.Any(),
Arg<double>.Any(),
Arg<bool[]>.Any())
.Returns((out int o, ref string r, in double d, bool[] args) => { o = 5; return 99; })
.Callback((out int o, ref string r, in double d, bool[] args) => { o = 5; /* side-effects */ });
Sequence-local callbacks¶
Callbacks are scoped to the sequence step they’re defined on. Use Then() to create a new step with independent callbacks.
Example
var seen = new List<string>();
imposter.Increment(Arg<int>.Any())
.Returns(_ => 10)
.Callback(_ => seen.Add("first"))
.Then()
.Returns(_ => 20)
.Callback(_ => seen.Add("second"));
service.Increment(1); // adds "first"
service.Increment(2); // adds "second"
Callbacks that throw¶
If a callback throws, the exception is propagated to the caller after the result has been produced.
Example
// See more examples in repo tests
imposter.GetNumber()
.Returns(1)
.Callback(() => throw new InvalidOperationException("boom"));
// service.GetNumber() returns 1, then throws InvalidOperationException
Async methods¶
For a method that returns Task, Task<T>, ValueTask or ValueTask<T>, a callback returns a Task. The callbacks still start when the method is called, in the order they were added. The task the method returns completes only after the arranged result and every callback's task have completed, and it fails with the first failure among them. Callbacks that finish synchronously leave the arranged task as it is.
Example
imposter.SaveAsync(Arg<string>.Any())
.ReturnsAsync(1)
.Callback(async name =>
{
await Task.Yield();
saved.Add(name);
});
await service.SaveAsync("report"); // completes after the callback; fails if the callback fails