Scott Anderson
Scott Anderson

Reputation: 984

How do you mock adding items to a repository or DbContext using moq?

The examples I've seen for using moq for a repository only show how to mock things being returned. I have a somewhat strange requirement: when a query is executed, if a condition exists, a certain item should be added to the repository. I am wondering how to test this without querying the database. I know how to mock the condition existing, but then how do you setup the mock so that you can test that the certain item is added?

Upvotes: 1

Views: 3705

Answers (5)

InteXX
InteXX

Reputation: 6377

You can do this by mocking the DbSet.Add() method, like so:

[Fact]
public void CreateBlog_saves_a_blog_via_context()
{
  var data = new List<Blog>
  {
    new Blog { Name = "BBB" },
    new Blog { Name = "ZZZ" },
    new Blog { Name = "AAA" },
  };
  
  var mockSet = new Mock<DbSet<Blog>>();
  mockSet.Setup(blogs => blogs.Add(It.IsAny<Blog>)).Returns<Blog>(blog =>
  {
    data.Add(blog);
    return blog;
  });
  
  var mockContext = new Mock<BloggingContext>();
  mockContext.Setup(m => m.Blogs).Returns(mockSet.Object);
  
  var service = new BlogService(mockContext.Object);
  var blog = service.AddBlog("_ADO.NET Blog", "http://blogs.msdn.com/adonet");
  var blogs = service.GetAllBlogs();
  
  mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once());
  mockContext.Verify(m => m.SaveChanges(), Times.Once());
  
  Assert.NotNull(blog)
  Assert.Equal(4, blogs.Count);
  Assert.Equal("AAA", blogs(1).Name);
  Assert.Equal("BBB", blogs(2).Name);
  Assert.Equal("ZZZ", blogs(3).Name);
}

This is adapted from the documentation found here.

Upvotes: 0

Dan Esparza
Dan Esparza

Reputation: 28385

Times have changed -- since the release of Entity Framework 6 it has become much easier to mock database context and datasets. This article outlines the particulars.

Testing non-query scenarios

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Moq; 
using System.Data.Entity; 

namespace TestingDemo 
{ 
    [TestClass] 
    public class NonQueryTests 
    { 
        [TestMethod] 
        public void CreateBlog_saves_a_blog_via_context() 
        { 
            var mockSet = new Mock<DbSet<Blog>>(); 

            var mockContext = new Mock<BloggingContext>(); 
            mockContext.Setup(m => m.Blogs).Returns(mockSet.Object); 

            var service = new BlogService(mockContext.Object); 
            service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet"); 

            mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); 
            mockContext.Verify(m => m.SaveChanges(), Times.Once()); 
        } 
    } 
}

Testing query scenarios Query testing is pretty sweet now, because you can build up test data sets in code and then execute your tests against them:

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Moq; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 

namespace TestingDemo 
{ 
    [TestClass] 
    public class QueryTests 
    { 
        [TestMethod] 
        public void GetAllBlogs_orders_by_name() 
        { 
            var data = new List<Blog> 
            { 
                new Blog { Name = "BBB" }, 
                new Blog { Name = "ZZZ" }, 
                new Blog { Name = "AAA" }, 
            }.AsQueryable(); 

            var mockSet = new Mock<DbSet<Blog>>(); 
            mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); 
            mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); 
            mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); 
            mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(0 => data.GetEnumerator()); 

            var mockContext = new Mock<BloggingContext>(); 
            mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); 

            var service = new BlogService(mockContext.Object); 
            var blogs = service.GetAllBlogs(); 

            Assert.AreEqual(3, blogs.Count); 
            Assert.AreEqual("AAA", blogs[0].Name); 
            Assert.AreEqual("BBB", blogs[1].Name); 
            Assert.AreEqual("ZZZ", blogs[2].Name); 
        } 
    } 
}

Upvotes: 1

Tim Long
Tim Long

Reputation: 13798

This blog article might be of use, although my design has changed somewhat since I wrote the post and I really need to update it. I used teh generic repository pattern in a way that enables the DbContext to be mocked. This allows the data access layer to be tested 'right up to the edges'. Generic Repository and Unit of Work

Upvotes: 1

testCoder
testCoder

Reputation: 7385

Try to use fake in memory repository instead of moq, for example universal generic repository for all entities:

public interface IInMemoryRepository<T> where T : class
{
    IQueryable<T> GetAll();
    void Create(T item);
    void Update(T item);
    T GetItem(Expression<Func<T, bool>> expression);
    void Delete(T item);
}

public class InMemoryRepository<T> : IInMemoryRepository<T> where T : class
{
    private int _incrementer = 0;
    public Dictionary<int, T> List = new Dictionary<int, T>();

    public IQueryable<T> GetAll()
    {
        return List.Select(x => x.Value).AsQueryable();
    }

    public void Create(T item)
    {
        _incrementer++;
        item.GetType().GetProperties().First(p => p.Name == "Id").SetValue(item, _incrementer, null);
        List.Add(_incrementer, item);
    }

    public void Update(T item)
    {
        var key = (int)item.GetType().GetProperties().First(p => p.Name == "Id").GetValue(item, null);
        List[key] = item;
    }

    public T GetItem(Expression<Func<T, bool>> expression)
    {
        return List.Select(x => x.Value).SingleOrDefault(expression.Compile());
    }

    public void Delete(T item)
    {
        var key = (int)item.GetType().GetProperties().First(p => p.Name == "Id").GetValue(item, null);
        List.Remove(key);
    }
}

Upvotes: 1

Andrew Barber
Andrew Barber

Reputation: 40160

You would not mock the repository; you would have an alternate repository that would use an in-memory store instead of the database, then use IoC to select the correct repository implementation for tests/code.

Upvotes: 1

Related Questions