简体   繁体   中英

Mocking a Service using Moq in C#

I have a service that is setup in this way.

public Interface IDataService : IDisposable
{
    IQueryable<T> Set<T>() where T : class;

    IDbSet<T> WritableSet<T>() where T : class;
}

IDataService is inherited by DataService.

public abstract class DataService : IDataService
{
    public IDataContext DataContext { get; private set; }

    public IQueryable<T> Set<T>() where T : class
    {
        return DataContext.Set<T>().AsNoTracking();
    }

    public IDbSet<T> WritableSet<T>() where T : class
    {
        return DataContext.Set<T>();
    }

    public AddResult<T> Add<T>(T obj) where T : class, IPersistentEntity
    {
        if (obj == null)
            return new AddResult<T>() { IsValid = false };
        else
        {
            if (obj.Id == Guid.Empty)
                WritableSet<T>().Add(obj);

            bool success = DataContext.SaveChanges() > 0;

            return new AddResult<T>() { Entity = obj, IsValid = success };
        }
    }
}

And The DataService is inherited by EntityService.

public class EntityService : DataService
{
    public EntityService(IDataContext DataContext) : base(DataContext)
    {
    }

    public void EntityStarted(Guid Id)
    {
        var a = GetWriteableById<Entity>(Id);
        a.Status = 1;
        DataContext.SaveChanges();
    }
}

This EntityService is used in one of my components. EntityService's object is created and passed to the component's constructor.

I'm using Moq to perform some tests on the component and for that, the plan was to mock the EntityService such that the EntityService uses a fake db container with dummy data for database like operations. But, I'm not having the best idea to mock this with minimum amount of new code.

The least appealing idea that I have is to create a fake EntityService class using the interface and have it's own implementation suitable for tests.

Help is appreciated! :)

As per @JLe and @Chetan's comment on the question, I had to mock the DbContext. I followed this article to mock the DbContext.

Mocking DbContext with Moq

Here is how the code looks like.

    private void Setup()
    {
        List<Entity> entityData = new List<Entity>();
        entityData.Add(new Entity
        {
            Id = Guid.NewGuid()
        });

        DbSet<Entity> MockEntitySet = GetSet(entityData);

        MockContext = new Mock<IDbContext>();
        MockContext.Setup(m => m.Set<Entity>()).Returns(MockEntitySet);
    }

    public static DbSet<T> GetSet<T>(List<T> sourceList) where T : class
    {
        return GetSet(sourceList.ToArray());
    }

    public static DbSet<T> GetSet<T>(T[] sourceList) where T : class
    {
        var name = typeof(T).Name;

        var queryable = sourceList.AsQueryable();

        Mock<DbSet<T>> dbSet = new Mock<DbSet<T>>();

        dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
        dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
        dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
        dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());

        dbSet.Setup(m => m.AsNoTracking()).Returns(dbSet.Object);

        return dbSet.Object;
    }

    [TestMethod]
    public void Test()
    {
        EntityService service = new EntityService(MockContext.Object);
        ComponentToTest compObj = new ComponentToTest(service);
        compObj.MethodToTest(...);

        // Assertions
    }

Thank you guys! Appreciate your help and suggestions.

To mock you need an Interface, if not, you need to mark the methods that you want to mock as virtual.

Under the hood the mocking framework is going to create a new implementation for you that behaves as you configured the mock.

Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM