3

I am using .NET4.5, EF6, and Moq for unit testing. I am trying to mock up some Db data for test. I have an example how to do this with declaring mockset as variable and then using mocks.

public static class TestExtensionMethods
{
       public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
       {
           var queryable = sourceList.AsQueryable();

           var 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(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
           return dbSet.Object;
       }
}

I am using it as extension method ATM like: mockedDbContext.Journey = new List<Journey> { }.AsDbSet();

is there a way to declare it all as one-liner (so I could pass db sets as TestCaseData without need to write extension methods). I tried following

var mockedDbContext = new Mock<OnlineLegal>();
mockedDbContext.Setup(o => o.Journey).Returns(() => (DbSet<Journey>)(new List<Journey> { new Journey { SessionId = sessionId, ConveyancingAnswer = new Collection<ConveyancingAnswer>()} }.AsEnumerable()));

but it blows up with System.InvalidCastException : Unable to cast object of type 'System.Collections.Generic.List1[Saga.Services.Legal.Website.Journey]' to type 'System.Data.Entity.DbSet1[Saga.Services.Legal.Website.Journey]'.

How do I mock DbSet<T> inline?

EDIT: In regards to duplication the guys who marked my question did not put effort into reading any further than title, and will get sarin gas from Santa this year.

Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265

2 Answers2

2

Use the method that you defined to create a DbSet from an List in this case where T is the type Journey and assuming o.Journey is of type DbSet<Journey>.

private DbSet<T> ToDbSet<T>(List<T> sourceList) where T : class
{
    var queryable = sourceList.AsQueryable();

    var 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(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
    return dbSet.Object;
}

private void SomeOtherMethod() 
{
    var journey = new Journey 
    { 
        SessionId = sessionId, 
        ConveyancingAnswer = new Collection<ConveyancingAnswer>()
    };
    var journeys = new List<Journey> { journey };  
    mockedDbContext.Setup(o => o.Journey)
                   .Returns(() => ToDbSet<Journey>(journeys));
}

If you wanted to put this on one line then it would be possible but not necessarily desirable in terms of readability.

silleknarf
  • 1,219
  • 6
  • 22
  • Hi, silleknarf thanks for response. Where in your answer can I find DbSet being declared inline without using extension methods? – Matas Vaitkevicius Dec 17 '15 at 13:24
  • `TestExtensionMethods.AsDbSet(journeys)` returns a `DbSet` – silleknarf Dec 17 '15 at 13:25
  • I've now removed the use of extension methods in my answer – silleknarf Dec 17 '15 at 13:35
  • sillknarf I really appreciate your effort and in no way want to discourage you but your answer is just a repetition of my question with some transformation, what I am looking for is how to declare DbSet on the fly without having to write additional mechanisms, like declaring list and then converting it somehow, one could go as simple as `new List().Select(o => new DbSet(o))` or smth like that but DbSet constructor is internal so question is quite complicated and there might simply be no way of doing it. Thanks for trying though. – Matas Vaitkevicius Dec 17 '15 at 13:49
1

I've been working on this topic a couple of hours yesterday. The anwser of @silleknarf is correct, but not complete. Async methods will give an exception. I've scoured the web yesterday for and anwser, and came to this extension method as solution. It works for dotnet 3.1. Previous versions might need different implementation due to some changes made to the interfaces. Hopefully it's useful for some of you. :)

public static class DbSet
{
    public static DbSet<T> ToDbSet<T>(this List<T> sourceList) where T : class
    {
        var queryable = sourceList.AsQueryable();
        var dbSet = new Mock<DbSet<T>>();

        dbSet.As<IAsyncEnumerable<T>>()
             .Setup(m => m.GetAsyncEnumerator(It.IsAny<CancellationToken>()))
             .Returns(new MockAsyncEnumerator<T>(queryable.GetEnumerator()));

        dbSet.As<IQueryable<T>>()
            .Setup(m => m.Provider)
            .Returns(new MockAsyncQueryProvider<T>(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(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
        return dbSet.Object;
    }
    private class MockAsyncQueryProvider<TEntity> : IAsyncQueryProvider
    {
        private readonly IQueryProvider _inner;

        internal MockAsyncQueryProvider(IQueryProvider inner)
        {
            _inner = inner;
        }

        public IQueryable CreateQuery(Expression expression)
        {
            return new MockAsyncEnumerable<TEntity>(expression);
        }

        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            return new MockAsyncEnumerable<TElement>(expression);
        }

        public object Execute(Expression expression)
        {
            return _inner.Execute(expression);
        }

        public TResult Execute<TResult>(Expression expression)
        {
            return _inner.Execute<TResult>(expression);
        }

        public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
        {
            var expectedResultType = typeof(TResult).GetGenericArguments()[0];
            var executionResult = typeof(IQueryProvider)
                     .GetMethod(name: nameof(IQueryProvider.Execute), genericParameterCount: 1, types: new[] { typeof(Expression) })
                     .MakeGenericMethod(expectedResultType)
                     .Invoke(this, new[] {expression});

            return (TResult)typeof(Task)
                .GetMethod(nameof(Task.FromResult))
                ?.MakeGenericMethod(expectedResultType)
                .Invoke(null, new[] { executionResult });
        }
    }
    private class MockAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
    {
        IQueryProvider IQueryable.Provider => new MockAsyncQueryProvider<T>(this);

        public MockAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { } 
        public MockAsyncEnumerable(Expression expression) : base(expression) { } 

        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
        {
            return new MockAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
        }
    }
    private class MockAsyncEnumerator<T> : IAsyncEnumerator<T>
    {
        private readonly IEnumerator<T> _inner;

        public T Current => _inner.Current;

        public MockAsyncEnumerator(IEnumerator<T> inner)
        {
            _inner = inner;
        }

        public ValueTask<bool> MoveNextAsync()
        {
            return new ValueTask<bool>(_inner.MoveNext());
        }
        public ValueTask DisposeAsync()
        {
            _inner.Dispose();

            return new ValueTask();
        }
    }
}
Nieksa
  • 354
  • 1
  • 7
  • 20