使用内存进行单元测试.ToListAsync() [英] Unit-testing .ToListAsync() using an in-memory

查看:84
本文介绍了使用内存进行单元测试.ToListAsync()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是由于内存数据库dbset不支持.ToListAsync()而导致的.ShouldNotThrow()测试失败的一种测试(我没有确切的用词,但您知道了).如果有任何重要意义,我正在尝试模拟Entity Framework ver提供的dbset. 6.1.3:

Below is the kind of test that is failing upon .ShouldNotThrow() due to .ToListAsync() not being supported by in-memory dbsets (I don't have the exact wording handy but you get the picture). In case it's of any importance, I'm trying to mockup the dbset provided by Entity Framework ver. 6.1.3:

[TestFixture]
public class Tests
{
    private SomeRepository _repository;
    private Mock<DbSet<SomeEntity>> _mockDbSet;
    private Mock<IApplicationDbContext> _mockAppDbContext;

    [OneTimeSetUp]
    public void TestFixtureSetUp()
    {
        _mockDbSet = new Mock<DbSet<SomeEntity>>();
        _mockAppDbContext = new Mock<IApplicationDbContext>();
        _mockAppDbContext.SetupGet(c => c.Gigs).Returns(_mockGigsDbSet.Object);

        _repository = new SomeRepository(_mockAppDbContext.Object);
    }

    [Test]
    public void Test()
    {
        // Setup
        var results = (IEnumerable<SomeEntity>) null;
        var singleEntity = new SomeEntity {Id = "1"};
        _mockDbSet.SetSource(new List<SomeEntity> { singleEntity });

        // Act
        var action = new Func<Task>(async () =>
        {
            results = await _repository.GetMultipleAsync(); //this ends up calling "await mockDbSet.ToListAsync().ConfigureAwait(false)" internally
        });

        // Verify
        action.ShouldNotThrow(); //an exception is thrown about .ToListAsync() not being supported by in-memory dbsets or something to that effect
        results.Should().BeEmpty();
    }
}

如果同步使用.ToList()代替基于异步的.ToListAsync(),则上述测试将按预期工作.当从实际的asp.net中使用该存储库时,它也可以正常工作.

The above test works as intended if .ToList() is used synchronously in place of the async-based .ToListAsync(). Also the repository works fine when used from within the actual asp.net.

那么,模拟dbset以便.ToListAsync()在这些单元测试中工作的正确方法是什么?

So what's the correct way to go about mocking up the dbset for .ToListAsync() to work in these unit-tests?

P.S .:我一直在进行单元测试的项目可以在这里找到:

P.S.: The project I've been unit-testing can be found here:

       https://bitbucket.org/dsidirop/gighub

由于.ToListAsync()而失败的单元测试会被标记为暂时失败".

The unit-tests that fail due to .ToListAsync() are marked with a comment 'fails for the time being'.

推荐答案

请布拉德福德·狄龙(Bradford Dillon)提供正确答案:

Hats off to Bradford Dillon for providing the correct answer:

https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx

为存储库的异步方法创建单元测试的正确方法是首先创建以下实用程序模型类:

The way proper way to create unit tests for async methods of repositories is to first create these utility-mockup classes:

using System.Collections.Generic; 
using System.Data.Entity.Infrastructure; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestingDemo 
{ 
    internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider 
    { 
        private readonly IQueryProvider _inner; 

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

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

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

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

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

        public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) 
        { 
            return Task.FromResult(Execute(expression)); 
        } 

        public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) 
        { 
            return Task.FromResult(Execute<TResult>(expression)); 
        } 
    } 

    internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T> 
    { 
        public TestDbAsyncEnumerable(IEnumerable<T> enumerable) 
            : base(enumerable) 
        { } 

        public TestDbAsyncEnumerable(Expression expression) 
            : base(expression) 
        { } 

        public IDbAsyncEnumerator<T> GetAsyncEnumerator() 
        { 
            return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); 
        } 

        IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() 
        { 
            return GetAsyncEnumerator(); 
        } 

        IQueryProvider IQueryable.Provider 
        { 
            get { return new TestDbAsyncQueryProvider<T>(this); } 
        } 
    } 

    internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> 
    { 
        private readonly IEnumerator<T> _inner; 

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

        public void Dispose() 
        { 
            _inner.Dispose(); 
        } 

        public Task<bool> MoveNextAsync(CancellationToken cancellationToken) 
        { 
            return Task.FromResult(_inner.MoveNext()); 
        } 

        public T Current 
        { 
            get { return _inner.Current; } 
        } 

        object IDbAsyncEnumerator.Current 
        { 
            get { return Current; } 
        } 
    } 
}

然后像这样使用它们:

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

namespace TestingDemo 
{ 
    [TestClass] 
    public class AsyncQueryTests 
    { 
        [TestMethod] 
        public async Task GetAllBlogsAsync_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<IDbAsyncEnumerable<Blog>>() 
                .Setup(m => m.GetAsyncEnumerator()) 
                .Returns(new TestDbAsyncEnumerator<Blog>(data.GetEnumerator())); 

            mockSet.As<IQueryable<Blog>>() 
                .Setup(m => m.Provider) 
                .Returns(new TestDbAsyncQueryProvider<Blog>(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(data.GetEnumerator()); 

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

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

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

这篇关于使用内存进行单元测试.ToListAsync()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆