Skip to content

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