Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I go about unit testing with Entity Framework and Moq?

I'm new to Moq, and wanting to use it like a backing store for data - but without touching the live database.

My setup is as follows:

  • A UnitOfWork contains all repositories, and is used for data access throughout the application.
  • A Repository represents a direct hook into a DbSet, provided by a DbContext.
  • A DbContext contains all DbSets.

Here is my test so far:

        // ARRANGE
        var user = new User()
        {
            FirstName = "Some",
            LastName = "Guy",
            EmailAddress = "[email protected]",
        };

        var mockSet = new MockDbSet<User>();
        var mockContext = new Mock<WebAPIDbContext>();

        mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

        // ACT
        using (var uow = UnitOfWork.Create(mockContext.Object))
        {
            uow.UserRepository.Add(user);
            uow.SaveChanges();
        }

        // ASSERT
        mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());

My test seems to be successful, as it can verify that a user was added to the mock DbSet - but what I need to do is actually get that data back and perform further assertions on it (this is just an ad-hoc test).

Please advise, testing frameworks are doing my head in. Also, I have the option to move to other testing frameworks if they are easier to use.

Thank you.

Update: Here is my working code.

Unit Test

        // ARRANGE
        var user = new User()
        {
            FirstName = "Some",
            LastName = "Guy",
            EmailAddress = "[email protected]",
        };

        var mockSet = new MockDbSet<User>();
        var mockContext = new Mock<WebAPIDbContext>();

        mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

        // ACT
        using (var uow = UnitOfWork.Create(mockContext.Object))
        {
            uow.UserRepository.Add(user);
            uow.SaveChanges();
        }

        // ASSERT
        mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());

        // TODO: Further assertations can now take place by accessing mockSet.BackingStore.
    }

MockDbSet

class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class
{
    public ICollection<TEntity> BackingStore { get; set; }

    public MockDbSet()
    {
        var queryable = (this.BackingStore ?? (this.BackingStore = new List<TEntity>())).AsQueryable();

        this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);
        this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
        this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
        this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator());

        // Mock the insertion of entities
        this.Setup(e => e.Add(It.IsAny<TEntity>())).Returns((TEntity entity) =>
        {
            this.BackingStore.Add(entity);

            return entity;
        });

        // TODO: Other DbSet members can be mocked, such as Remove().
    }
}
like image 650
Rhonage Avatar asked Aug 08 '17 02:08

Rhonage


People also ask

Is Moq a unit test?

The Moq framework is an open source unit testing framework that works very well with . NET code and Phil shows us how to use it.

What is the difference between NUnit and Moq?

NUnit is a unit testing framework for . NET languages, and Moq is the most popular mocking framework for . NET. I assume the reader is familiar with C# projects, Visual Studio IDE, and the Nuget package manager for managing dependencies.

Why should you use the Moq framework for testing?

The Moq framework makes it easy to create mock objects that mimic the behavior of classes and interfaces for testing, with just the functionality you need.


1 Answers

You just need to create a collection to act as the backing store and mock the enumeration db set with the backing collection

public class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class {
    public MockDbSet(List<TEntity> dataSource = null) {
        var data = (dataSource ?? new List<TEntity>());
        var queryable = data.AsQueryable();

        this.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);
        this.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
        this.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
        this.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(() => queryable.GetEnumerator());
        //Mocking the insertion of entities
        this.Setup(_ => _.Add(It.IsAny<TEntity>())).Returns((TEntity arg) => {
            data.Add(arg);
            return arg;
        });

        //...the same can be done for other members like Remove
    }
}

So now you can use a list to hold the data

// ARRANGE
var dataSource = new List<User>(); //<-- this will hold data
var user = new User()
{
    FirstName = "Some",
    LastName = "Guy",
    EmailAddress = "[email protected]",
};

var mockSet = new MockDbSet<User>(dataSource);
var mockContext = new Mock<WebAPIDbContext>();

mockContext.Setup(c => c.Set<User>()).Returns(mockSet.Object);

// ACT
using (var uow = UnitOfWork.Create(mockContext.Object))
{
    uow.UserRepository.Add(user);
    uow.SaveChanges();


    // ASSERT
    mockSet.Verify(u => u.Add(It.IsAny<User>()), Times.Once());
    Assert.IsTrue(dataSource.Contains(user)); //<-- shows mock actually added item
    Assert.IsTrue(uow.UserRepository.Any(u => u == user)); //<-- show you can actually query mock DbSet
}
like image 148
Nkosi Avatar answered Nov 12 '22 03:11

Nkosi