Question

I need to unit test an inner join method from my Data Access Layer.

MY DAL looks like this:

public class MyDAL:IMyDAL
{
    private MyContext context;

public MyDAL(MyContext Context)
    {
        this.context = Context;
    }
public IEnumerable <Parent> Read(int childId)
    {
        var query = from parent in context.Parent
                    join child in context.Child on parent.Id equals child.Id
                    where child.Id == childId
                    select env;
        return query.ToList();
    }

And I want to unit test the Read method by mocking Entity framework, following this link http://msdn.microsoft.com/en-us/data/dn314429.aspx.

  [TestMethod]
    public void ReadMethod()
    {

        var data = new List<Parent> 
        { 
            new Parent { Id = 20 },
        }.AsQueryable();
        var data2 = new List<Child> 
        { 
            new Child { Id = 8 },
        }.AsQueryable();


        var mockSetPar = new Mock<DbSet<Parent>>();
        var mockSetChild = new Mock<DbSet<Child>>();

        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        moockSetChild.As<IQueryable<Child>>().Setup(m => m.Provider).Returns(data2.Provider);
        mockSetChild.As<IQueryable<Child>>().Setup(m => m.Expression).Returns(data2.Expression);
        mockSetChild.As<IQueryable<Child>>().Setup(m => m.ElementType).Returns(data2.ElementType);
        mockSetChild.As<IQueryable<Child>>().Setup(m => m.GetEnumerator()).Returns(data2.GetEnumerator());


        var customDbContextMock = new Mock<MyContext>();
        customDbContextMock.Setup(x => x.Parent).Returns(mockSetPar.Object);
        customDbContextMock.Setup(x => x.Child).Returns(mockSetChild.Object);




        var myDAL = new MyDAL(customDbContextMock.Object);


        var actual = myDAL.Read(8);
          Assert.IsNotNull(actual);

The result actual is empty because the join method hasn't been mocked so it returns nothing.

How can I mock the join method to return a value?

Thank you

Was it helpful?

Solution 2

I think you already noticed, that mocking EF queries is time-consuming and brittle. My suggestion - do not mock it. You can hide as much of data-access logic as you can under repository interfaces which is easy to mock:

public interface IParentRepository
{
    IEnumerable<Parent> GetParentsOfChild(int childId);
}

Then test will look like:

[TestMethod]
public void ReadMethod()
{
    int childId = // get sample id
    var expected = // get sample parents

    var repositoryMock = new Mock<IParentRepository>();
    repositoryMock.Setup(r => r.GetParentsOfChild(childId))
                  .Returns(expected);

    var myDAL = new MyDAL(repositoryMock.Object);
    var actual = myDAL.Read(childId);

    repositoryMock.VerifyAll();
    CollectionAssert.AreEqual(actual, expected);
}

If you want to verify query implementation, then best way to do this is an acceptance/integration test which involves real database. Keep in mind - analyzing generated IQueryable is not enough to be sure your query will work in real environment. E.g. you can use operator Last() with IQueryable but this query will fail to be translated to SQL by Entity Framework.

OTHER TIPS

In-memory test of DB interactions might be misleading because the LINQ to Entities capabilities are a subset of LINQ to Objects capabilities, so you can write a test that will be green but the query will always throw an exception in production.

This is also wrong on sole conceptual level. The DAL is a code that lays on a boundry of two systems - app and db. Its responsibility is to integrate them. So if you isolate those integration components your test becomes meaningless as it mocks away the core responsibility of the SUT.

To test the query logic, you have to use a database provider that behaves like the production one. So you need integration tests. = A useful solution is to use SQLite in-memory database. It will behave as the real database in most scenarios covered by Entity Framework yet perform almost as fast as mocks based on in-memory collections in unit tests.

You can consider SQLite as a database mock on steroids if you like.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top