NSubstitute icon indicating copy to clipboard operation
NSubstitute copied to clipboard

Add overload for AndDoes that supports Callback like When...Do

Open gtbuchanan opened this issue 6 years ago • 2 comments

Right now, there doesn't seem to be support for complex callbacks with AndDoes like there is with When...Do. This leads to some redundant code if you want to configure specific callbacks with multiple return results.

Example:

var sut = Substitute.For<DbDataReader>();
sut.ReadAsync(Arg.Any<CancellationToken>()).Returns(true, true, true, false);
sut.When(r => r.ReadAsync(Arg.Any<CancellationToken>()))
    .Do(Callback.First(_ => /* Do something only on the first call */));

It would be great to have an overload of AndDoes that supports Callback so things like this could be simplified to:

sut.ReadAsync(Arg.Any<CancellationToken>())
    .Returns(true, true, true, false);
    .AndDoes(Callback.First(_ => /* Do something only on the first call */));

gtbuchanan avatar Jun 04 '18 16:06 gtbuchanan

Thanks for raising this. 👍

Would this be covered sufficiently by putting the callbacks within the Returns?

sut.ReadAsync(Arg.Any<CancellationToken>())
    .Returns(
        x => { doSomethingOnFirstCall(); return true }, 
        x => true, 
        x => true, 
        x => false);

I initially considered a similar builder-style syntax for return values adapted to specify both returns and callbacks, but am wondering if that provides much benefit over the existing Returns functionality.

dtchepak avatar Jun 04 '18 23:06 dtchepak

It would cover my example, but I find it semantically awkward. Now it's passing 4 lambdas even though 3 are static values. It wouldn't cover Callback.First(...).ThenKeepDoing(...) very well as you'd have to repeat the second action in every subsequent delegate, which would end up more messy than a separate When...Do(Callback).

I don't think a new builder would be necessary since Callback already exists. Plus, it just feels wrong to me to perform a side-effect in the Returns method anyway. I do use the delegate-style Returns when I have to return a value based on the CallInfo, but I still like the idea of a new overload of AndDoes to mirror the Do(Callback) overload.

gtbuchanan avatar Jun 05 '18 15:06 gtbuchanan