Here's the strange thing - when I run the Unit Test and I put the breakpoint on the line return Unit.Value;
, then I see that the someResult
value is null - but when I manually move the debugger's yellow arrow the the previous line again var someResult = await _myRepository.DoSomething(cancellationToken);
then the someResult
variable is not null and contains my object - why does that happen ?
Unit test snippet:
//Arrange
var myList = new List<MyTable> { new MyTable() };
var myRepository = Substitute.For<IMyRepository>();
myRepository.DoSomething(Arg.Any<CancellationToken>()).Returns(myList);
var command = Substitute.For<MyCommand>();
//Act
var sut = new MyCommandHandler(myRepository);
await sut.Handle(command, Arg.Any<CancellationToken>());
I also tried:
myRepository.DoSomething(Arg.Any<CancellationToken>()).Returns(Task.FromResult(myList));
The tested class:
public class MyCommandHandler : ICommandHandler<MyCommand, Unit>
{
private readonly IMyRepository _myRepository;
public MyCommandHandler(IMyRepository myRepository)
{
_myRepository = myRepository ?? throw new ArgumentNullException(nameof(myRepository));
}
public async Task<Unit> Handle(MyCommand command, CancellationToken cancellationToken)
{
var someResult = await _myRepository.DoSomething(cancellationToken);
...
return Unit.Value;
}
}
public class MyRepository : IMyRepository
{
private readonly MyDbContext _context;
public MyRepository(MyDbContext context)
{
_context = context;
}
public async Task<List<MyTable>> DoSomething(CancellationToken cancellationToken = default)
{
return await _context.MyTable
.AsNoTracking()
.Where(...)
.ToListAsync(cancellationToken);
}
}
I think the Arg.Any<CancellationToken>()
matcher in your Handle
invocation is the problem.
Changing
await sut.Handle(command, Arg.Any<CancellationToken>());
to
await sut.Handle(command, default);
got the test working. The matchers are for configuring the substitute and checking the received invocations, not for the actual invocation itself.
A working example can be found here. I didn't have enough to go on to replicate Unit
and it's usage but was able to observe and resolve the same problem you were having without it.