How do I mock AddAsync?

Not pretty, but this was the only way I found to mock AddAsync and make it return an actual value:

mockWebJobDbSet
.Setup(_ => _.AddAsync(It.IsAny<WebJobStatus>(), It.IsAny<CancellationToken>()))
.Callback((WebJobStatus model, CancellationToken token) => { webjobStatusList.Add(model); })
.Returns((WebJobStatus model, CancellationToken token) => ValueTask.FromResult(new EntityEntry<WebJobStatus>
(new InternalEntityEntry(
    new Mock<IStateManager>().Object,
    new RuntimeEntityType("WebJobStatus", typeof(WebJobStatus), false, null, null, null, ChangeTrackingStrategy.Snapshot, null, false),
    model)
)));

You will need to return a Task to allow the async/await call

await _dbContext.WebJobStatus.AddAsync(newWebJobStatus);

to flow to completion.

So assuming that Add returns the object added

mockWebJobDbSet
    .Setup(_ => _.AddAsync(It.IsAny<WebJobStatus>(), It.IsAny<System.Threading.CancellationToken>()))
    .Callback((WebJobStatus model, CancellationToken token) => { webjobstatusList.Add(model); })
    .Returns((WebJobStatus model, CancellationToken token) => Task.FromResult((EntityEntry<WebJobStatus>)null));

Note that the method being Setup takes two arguments, so the Callback and Returns will need to expect two arguments as well if they want to use the captured arguments.


The way I have done mocking is the following:-

var myList = new List<MyClass>();
       
_dataContext.Setup(m => m.MyClasses.AddAsync(It.IsAny<MyClass>(), default))
            .Callback<Slide, CancellationToken>((s, token) => {myList.Add(s);});
        
_dataContext.Setup(c => c.SaveChangesAsync(default))
            .Returns(Task.FromResult(1))
            .Verifiable();

And the actual implementation is the following:-

public async Task<int> AddMyClassAsync(MyClass myClass)
{
    await _careMapsDataContext.MyClasses.AddAsync(myClass);
        
    return await _careMapsDataContext.SaveChangesAsync();
}

In .net core 3.1, I had to rewrite the Returns as below:

.Returns((T model, CancellationToken token) => new ValueTask<EntityEntry<T>>());