You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
And I'm having the exact same issue for another test, that inside call the context ad the .ToListAsync() method. I'll show the code of this other one so that there are two examples in place. I'm using .NET 8, NSubstitute 5.3.0 and CQRS pattern with MediatR (Command and Queries).
Here is the sample query I have in my code:
public sealed record GetAllTodosQuery : IQuery<List<TodoDto>>;
internal sealed class GetAllTodosQueryHandler(IApplicationDbContext context)
: IQueryHandler<GetAllTodosQuery, List<TodoDto>>
{
public async Task<Result<List<TodoDto>>> Handle(GetAllTodosQuery query, CancellationToken cancellationToken)
{
var entities = await context.Todos.ToListAsync(cancellationToken);
return Result<List<TodoDto>>.Success(entities.Adapt<List<TodoDto>>());
}
}
This is the attempt of writing a test for that use case:
public class GetAllTodosQueryTest
{
private readonly IApplicationDbContext _context;
private readonly GetAllTodosQueryHandler _handler;
public GetAllTodosQueryTest()
{
_context = Substitute.For<IApplicationDbContext>();
_handler = new GetAllTodosQueryHandler(_context);
}
[Fact]
public async Task Handler_ShouldReturnEmptyList_WhenNoEntitiesInsideDatabase()
{
// Arrange
List<TodoEntity> entities = [];
var cancellationToken = new CancellationTokenSource().Token;
_context.Todos.ToListAsync(Arg.Is(cancellationToken))
.Returns(entities);
var query = new GetAllTodosQuery();
// Act
var result = await _handler.Handle(query, CancellationToken.None);
// Assert
Assert.NotNull(result.Data);
Assert.Empty(result.Data);
}
}
I'm getting the same error that I get for the other test I showed inside stackoverflow:
NSubstitute.Exceptions.UnexpectedArgumentMatcherException: Argument matchers (Arg.Is, Arg.Any) should only be used in place of member arguments. Do no...
NSubstitute.Exceptions.UnexpectedArgumentMatcherException
Argument matchers (Arg.Is, Arg.Any) should only be used in place of member arguments. Do not use in a Returns() statement or anywhere else outside of a member call.
Correct use:
sub.MyMethod(Arg.Any<string>()).Returns("hi")
Incorrect use:
sub.MyMethod("hi").Returns(Arg.Any<string>())
at NSubstitute.Core.ThreadLocalContext.LastCallShouldReturn(IReturn value, MatchArgs matchArgs)
at NSubstitute.SubstituteExtensions.ConfigureReturn[T](MatchArgs matchArgs, T returnThis, T[] returnThese)
at NSubstitute.SubstituteExtensions.Returns[T](Task`1 value, T returnThis, T[] returnThese)
at Application.UnitTests.Todos.GetAllTodosQueryTest.Handler_ShouldReturnEmptyList_WhenNoEntitiesInsideDatabase() in /Users/manuelraso/Documents/repo/devops/templates/dotnet-8-minimal-api-cqrs-postgresql/Application.UnitTests/Todos/GetAllTodosQueryTest.cs:line 28
at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_0.<<InvokeTestMethodAsync>b__1>d.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 276
--- End of stack trace from previous location ---
at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in /_/src/xunit.execution/Sdk/Frameworks/ExecutionTimer.cs:line 48
at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in /_/src/xunit.core/Sdk/ExceptionAggregator.cs:line 90
I really don't understand what's going on, because I'm actually able to mock the FindAsync() method, but not the ToListAsync(). In another test I have this:
[Fact]
public async Task Handler_ShouldReturnSuccess_WhenTodoExists()
{
// Arrange
var guid = Guid.NewGuid();
var cancellationToken = new CancellationTokenSource().Token;
var entityFound = new TodoEntity
{
Id = guid,
Title = "Test Title",
Description = "Test description"
};
_context.Todos.FindAsync(Arg.Is(guid), Arg.Is(cancellationToken))
.Returns(entityFound);
var query = new GetTodoQuery(guid);
// Act
var result = await _handler.Handle(query, cancellationToken);
// Assert
Assert.NotNull(result.Data);
Assert.Equal(entityFound.Id, result.Data.Id);
Assert.Equal(entityFound.Title, result.Data.Title);
Assert.Equal(entityFound.Description, result.Data.Description);
}
and it's working like a charm. Am I doing something wrong? Is there a bug? How can I make this work?
The text was updated successfully, but these errors were encountered:
I think you have to learn how NSubstitute works. You can only mock methods that are part of the class/interface you are mocking.
ToListAsync is an extension method, which is a static method of a totally different class. You cannot mock these with any mocking library.
To get out, you have to make your abstraction of the DbContext really abstract or you have to handcraft your mock, e.g. by using an in memory database.
The goal of extensions methods is to make code more readable, but sometimes you forget that these are just static methods and static is normally the enemy of testability.
@ShadyManu thanks for the comprehensive example. 👍
This is definitely confusing if you haven't encountered this before. In addition to @Ergamon's advice, I recommend adding NSubstitute Analyzers to your test project to help detect these cases.
Hello everyone, I'm having issues with NSubstitute and EF method ToListAsync().
I have already opened the question in stackoverflow:
https://stackoverflow.com/questions/79263044/problem-with-tolistasync-and-nsubstitute-for-unittest
And I'm having the exact same issue for another test, that inside call the context ad the .ToListAsync() method. I'll show the code of this other one so that there are two examples in place. I'm using .NET 8, NSubstitute 5.3.0 and CQRS pattern with MediatR (Command and Queries).
Here is the sample query I have in my code:
This is the attempt of writing a test for that use case:
I'm getting the same error that I get for the other test I showed inside stackoverflow:
I really don't understand what's going on, because I'm actually able to mock the FindAsync() method, but not the ToListAsync(). In another test I have this:
and it's working like a charm. Am I doing something wrong? Is there a bug? How can I make this work?
The text was updated successfully, but these errors were encountered: