NSubstitute
NSubstitute copied to clipboard
Recording not only arguments but also return values from a (base) method calls.
Question The ICall interface provides information about recorded calls, but does not contain the return value for non-void method calls. Is it also possible to record/retrieve the return value(s) from ReceivedCalls() (or by using a When().Do() construct that saves the returned value, but does not alter it) ?
This would allow using a ForPartsOf<T> spy to record not only the arguments passed to a base method, but also its return value(s).
@rbeurskens not currently. Do you have a code example of where this would be helpful?
using NSubstitute;
using NSubstitute.Core;
namespace TestProject1;
public interface IMyData
{
bool EnableStuff { get; }
string Message { get; }
}
public interface IProcessor
{
void Process(object data);
void ProcessEx(object data);
string GetName();
}
public class MyClassToTest
{
public void TestAction(IMyData data, IProcessor processor)
{
Action1(data, processor);
if (!TryAction2(data, processor))
{
// any call to processor.Process() here would break current workaround.
}
Action3(data, processor);
}
protected internal virtual void Action1(IMyData data, IProcessor processor)
{
// Do stuff with data
processor.Process("stuff");
processor.Process(data.Message);
}
protected internal virtual bool TryAction2(IMyData data, IProcessor processor)
{
// action depends on state of data
// conditional inside method - it is always called, however
// I know the conditional can be moved to the outer method, but the inheritance design in actual code lets subtypes implement this method and return whether or not the operation was valid while the outer method is implemented in base.
if (data.EnableStuff)
{
// Do other stuff, implementation not relevant for test, only if it has been executed
processor.Process("stuff");
processor.Process(data.Message);
return true;
}
return false;
}
protected internal virtual void Action3(IMyData data, IProcessor processor)
{
// Do stuff with data
processor.Process("stuff");
processor.Process(data.Message);
}
}
public class Tests
{
[Test]
public void Action2ConditionalTest([Values(true,false)]bool doStuff)
{
// arrange
var processor = Substitute.For<IProcessor>();
var sut = Substitute.ForPartsOf<MyClassToTest>();
var data = Substitute.For<IMyData>();
data.Message.Returns("some message");
data.EnableStuff.Returns(doStuff);
// workaround to achieve this now
sut.WhenForAnyArgs(o => o.TryAction2(default, default))
.Do(_ => processor.ClearReceivedCalls()); // Executes BEFORE base is called
IEnumerable<ICall>? calls = null;
sut.WhenForAnyArgs(o => o.Action3(default, default))
.Do(_ => calls = processor.ReceivedCalls().ToList()); // Not sure if we can configure a callback that executes AFTER the base call for TryAction2, so use the next method to be executed to capture the calls at that point.
// act
sut.TestAction(data, processor);
// assert
sut.Received(1).Action1(data, processor);
// sut.Received(1).TryAction2(data, processor).Returned(doStuff); // something like this would be nice
sut.Received(1).TryAction2(data, processor);
sut.Received(1).Action3(data, processor);
// ... or using the equivalent Received.InOrder(() => { ... }) syntax
// assert using the saved CallInfo Action3 allowed us to capture. (but it could be there is no such method)
Assert.NotNull(calls);
Assert.That(calls.Any(ci => ci.GetArguments().Any()), Is.EqualTo(doStuff));
}
}
Hi @rbeurskens,
Thank you for the example. Could you help me understand the purpose of that check please.
As I understand you want to assert the return value of a method call (TryAction2()
) inside your SUT method (TestAction()
). I would personally write a separate unit test for that where it tests that method (TryAction2()
) in isolation. What am I missing?
Well, the idea here is to test the workflow implemented inside the TestAction()
method which is dependent on the IMyData
object that is passed in. And indeed I am testing if TryAction2()
returns what IMyData.EnableStuff
returns. I would think that since it's possible to record/save out parameter values, it would be nice to have this for return values too.
The IMyData
and IProcessor
here are simplified in this sample. The real implementation of what IProcessor
is here would call an external library or do some I/O operation. The point is there is no return value but I want to test the workflow.
In my case MyClassToTest
would represent a graphic object depending on the state of IMyData
for its appearance and using a IProcessor
implementing a rendering library, so I like to test if all (sub)operations for it to draw itself are followed correctly depending on IMyData
without having to do any actual rendering.
If that's the only way to go, or just common good practice, I might need some help on how to test that in isolation in my own code. Or maybe it's very simple and I'm overlooking it.