XUnit Service Layer Testing Core 3 System.ArgumentNullException

Viewed 329

Although I have experience with TDD in another language, I am having a difficult time understanding testing in .NET Core 3. I am using Entity Framework, and trying to produce tests on my service layer. I have seen a few examples with repositories and some based on API, but I haven't figured out what I'm doing wrong.

I'm getting a System.ArgumentNullException : Value cannot be null. (Parameter 'source') error when running the test.

ICustomerService.cs

public interface ICustomerService
{
    List<Customer> GetCustomers();
}

CustomerService.cs

public class CustomerService : ICustomerService
{
    private readonly DbContext _db;
    public CustomerService(DbContext db)
    {
        _db = db;
    }

    public List<Customer> GetCustomers()
    {
        return _db.Customer.ToList();
    }
}

CustomerServiceTests.cs

[Fact]
public void GetCustomersReturnsRecords()
  {
    // Arrange
    var cntxt = new Mock<DbContext>();
    CustomerService cs = new CustomerService(cntxt.Object);

    // Act
    var result = cs.GetCustomers();

    //Asserts here
    Assert.NotNull(result);
  }
1 Answers

Instead of trying to mock the DbContext, consider using an in-memory database.

Unit testing

Consider testing a piece of business logic that might need to use some data from a database, but is not inherently testing the database interactions. One option is to use a test double such as a mock or fake.

We use test doubles for internal testing of EF Core. However, we never try to mock DbContext or IQueryable. Doing so is difficult, cumbersome, and fragile. Don't do it.

Instead we use the EF in-memory database when unit testing something that uses DbContext. In this case using the EF in-memory database is appropriate because the test is not dependent on database behavior. Just don't do this to test actual database queries or updates.

Reference Testing code that uses EF Core

That is the suggest approach from documentation.

[Fact]
public void GetCustomersReturnsRecords() {
    // Arrange
    var builder = new DbContextOptionsBuilder<MyDbContext>();
    builder.UseInMemoryDatabase();
    var options = builder.Options;
    using(var context = new MyDbContext(options)) {
        CustomerService subject = new CustomerService(context);

        // Act
        var result = subject.GetCustomers();

        //Asserts here
        Assert.NotNull(result);
    }
}

Reference Testing with the EF In-Memory Database

Related