0

I'm learning how to unit test ASP.NET Core web applications by reading the docs and tutorials, but not progressing too much because the docs looks kinda silly to me. I'm following the right path?

I have this abstract class BaseRepository:

public abstract class BaseRepository<TEntity, TContext> 
    : IRepository<TEntity> 
    where TEntity : class, IBaseEntity
    where TContext : DbContext
{

    protected readonly TContext _context;

    protected BaseRepository(TContext context) 
    {
        _context = context;
    }

    public async Task<TEntity?> GetById(int id)
    {
        return await _context.Set<TEntity>().FindAsync(id);
    }

    public async Task<TEntity> Create(TEntity entity)
    {
        _context.Set<TEntity>().Add(entity);
        await _context.SaveChangesAsync();
        return entity;
    }

}

There is an UserRepository class that inherits from BaseRepository:

public class UserRepository : BaseRepository<User, TodoContext>
{
    public UserRepository(TodoContext context) : base(context)
    {}
}

And my test:

public class UnitTest1
{
    [Fact]
    public async Task Test1()
    {
        var userTestObject = new User
        {
            Id = 1,
            NickName = "johnDoe",
            Email = "johndoe@email.com",
            Password = "Pass@1234",
            UserTasks = new List<UserTask>()
        {
            new UserTask()
            {
                Id = 1,
                Description = "Study C#/.NET"
            }
        }
        };

        var repositoryMock = new Mock<IRepository<User>>();
        var contextMock = new Mock<TodoContext>();

        repositoryMock.Setup(m => m.Create(It.IsAny<User>()))
            .Returns(Task.FromResult(userTestObject));

        // Acts
        var repository = new UserRepository(contextMock.Object);
        await repository.Create(userTestObject);
        
        // Asserts
        repositoryMock.Verify(x => x.Create(It.Is<User>(y => y == userTestObject)));
    }
}

And the exception is being thrown at this line:

await repository.Create(userTestObject);

1 Answers1

2

You're creating a mock:

var repositoryMock = new Mock<IRepository<User>>();

...but that's not what you're testing. You're creating an instance of UserRepository and testing that. That's good. There's no reason to create a mock and run a test against it. You can just delete that mock and its setup from the test.

With that aside, the error is happening because the mock TodoContext isn't fully set up. When you get to this line in the repository:

_context.Set<TEntity>().Add(entity);

_context is another mock you created ._context.Set<TEntity>() returns null, so calling Add on it throws a NullReferenceException.

That's why you're getting the exception. The question then is, what do you change?

Here's a good answer that shows how to create a "real" DbSet with an in-memory database. You can inject that into your repository instead of a mocked DbSet.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • It passed, thanks! So, should i also discard Moq for my unit tests with services and controllers? Can you give me a use case where Moq would be more suitable than using an in-memory db? – johnjoker13 May 09 '23 at 18:01
  • 1
    No, Moq is a useful tool. That's a hard question. I'd use Moq when it's the easiest thing to use. Sometimes it's better to create a class or use a class. I avoid Moq if the setup gets complicated and hard to read, or if I'd have to create a mock that returns another mock. But sometimes it's easy. Here's a blog post. https://scotthannen.org/blog/2020/07/10/easier-to-mock-without-moq.html – Scott Hannen May 09 '23 at 18:24