单元文本模拟 dbContext
unit text mock dbContext
我尝试在 returns 可枚举的存储库上进行单一测试。但是我有下一个错误:
System.AggregateException : One or more errors occurred. (The source IQueryable doesn't implement IAsyncEnumerable<myNamespace.DTO.UserDTO>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.)
---- System.InvalidOperationException : The source IQueryable doesn't implement IAsyncEnumerable<myNamespace.DTO.UserDTO>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
这是我的单元测试:
//Arrange
var mockSet = Substitute.For<DbSet<User>, IQueryable<User>, IDbAsyncEnumerable<User>>();
((IDbAsyncEnumerable<User>)mockSet).GetAsyncEnumerator()
.Returns(new TestDbAsyncEnumerator<User>(GetUserList().AsQueryable().GetEnumerator()));
((IQueryable<User>)mockSet).Provider.Returns(new TestDbAsyncQueryProvider<User>(GetUserList().AsQueryable().Provider));
((IQueryable<User>)mockSet).Expression.Returns(GetUserList().AsQueryable().Expression);
((IQueryable<User>)mockSet).ElementType.Returns(GetUserList().AsQueryable().ElementType);
((IQueryable<User>)mockSet).GetEnumerator().Returns(GetUserList().AsQueryable().GetEnumerator());
var mockContext = Substitute.For<IMyContext>();
mockContext.Users.Returns(mockSet);
//Act
CancellationToken cancellationToken = new CancellationToken();
UserRepository userRepository = new UserRepository(mockContext);
var users = userRepository.GetListAsync(cancellationToken).Result;
//Assert
Assert.NotNull(users);
我想测试我的仓库:
public async Task<IEnumerable<UserDto>> GetListAsync(CancellationToken cancellationToken)
{
return await _myContext.Users.Select(u => new UserDto
{
Id = u.Id,
FistName = u.FistName ,
LastName = u.LastName
}).ToListAsync(cancellationToken);
}
有什么问题?
正如 OP 评论中所述,您所指的 doco 适用于 EF,而不是 EFCore。您需要实现一组不同的接口。
通常的建议是避免模拟 DbContext
但是在这种情况下,您可能需要这样做,因为内存提供程序不支持异步操作。我不确定 SQLite 是否支持它们。 EntityFrameworkCore.Testing 应该处理这种情况(免责声明,我是作者),但您需要使用上下文实现而不是接口。
实现此功能的最常见方法是创建异步接口的实现,其方式与 EF doco 相同,但适用于 EFCore。您会发现大多数 EFCore 模拟库都会这样做:
public class TestAsyncEnumerable<T> : IAsyncEnumerable<T>, IOrderedQueryable<T>
{
private readonly IEnumerable<T> _enumerable;
private readonly IQueryable<T> _queryable;
public TestAsyncEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
_queryable = _enumerable.AsQueryable();
ElementType = _queryable.ElementType;
Expression = _queryable.Expression;
Provider = new TestAsyncQueryProvider<T>(_queryable);
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return new TestAsyncEnumerator<T>(_queryable);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public TestAsyncEnumerator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
public TestAsyncQueryProvider(IQueryable<T> source)
{
Source = source;
}
private IQueryable<T> Source { get; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken())
{
throw new NotImplementedException();
}
}
这不是一个完整的实现,只是解决 OP 案例所需要的。重要的一点是这一行:
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
这将允许投影与异步操作一起工作。
工作示例:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using AutoFixture;
using KellermanSoftware.CompareNetObjects;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Internal;
using NSubstitute;
using NUnit.Framework;
namespace Question62783423
{
public class Tests
{
[Test]
public void Test1()
{
var fixture = new Fixture();
var users = new TestAsyncEnumerable<User>(fixture.CreateMany<User>());
//Arrange
var mockSet = Substitute.For<DbSet<User>, IQueryable<User>, IAsyncEnumerable<User>>();
((IAsyncEnumerable<User>) mockSet).GetAsyncEnumerator().Returns(users.GetAsyncEnumerator());
((IQueryable<User>) mockSet).Provider.Returns(users.Provider);
((IQueryable<User>) mockSet).Expression.Returns(users.Expression);
((IQueryable<User>) mockSet).ElementType.Returns(users.ElementType);
((IQueryable<User>) mockSet).GetEnumerator().Returns(((IQueryable<User>) users).GetEnumerator());
var mockContext = Substitute.For<IMyContext>();
mockContext.Users.Returns(mockSet);
//Act
var cancellationToken = new CancellationToken();
var userRepository = new UserRepository(mockContext);
var result1 = userRepository.GetListAsync(cancellationToken).Result;
var result2 = userRepository.GetListAsync(cancellationToken).Result;
var comparer = new CompareLogic();
comparer.Config.IgnoreCollectionOrder = true;
comparer.Config.IgnoreObjectTypes = true;
var comparisonResult1 = comparer.Compare(users, result1);
var comparisonResult2 = comparer.Compare(users, result2);
Assert.That(comparisonResult1.Differences.Any(), Is.False);
Assert.That(comparisonResult2.Differences.Any(), Is.False);
}
}
}
public class TestAsyncEnumerable<T> : IAsyncEnumerable<T>, IOrderedQueryable<T>
{
private readonly IEnumerable<T> _enumerable;
private readonly IQueryable<T> _queryable;
public TestAsyncEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
_queryable = _enumerable.AsQueryable();
ElementType = _queryable.ElementType;
Expression = _queryable.Expression;
Provider = new TestAsyncQueryProvider<T>(_queryable);
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return new TestAsyncEnumerator<T>(_queryable);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public TestAsyncEnumerator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
public TestAsyncQueryProvider(IQueryable<T> source)
{
Source = source;
}
private IQueryable<T> Source { get; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken())
{
throw new NotImplementedException();
}
}
public class User
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserDto
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IMyContext
{
DbSet<User> Users { get; set; }
}
public class UserRepository
{
private readonly IMyContext _myContext;
public UserRepository(IMyContext myContext)
{
_myContext = myContext;
}
public async Task<IEnumerable<UserDto>> GetListAsync(CancellationToken cancellationToken)
{
return await _myContext.Users.Select(u => new UserDto { Id = u.Id, FirstName = u.FirstName, LastName = u.LastName }).ToListAsync(cancellationToken);
}
}
我尝试在 returns 可枚举的存储库上进行单一测试。但是我有下一个错误:
System.AggregateException : One or more errors occurred. (The source IQueryable doesn't implement IAsyncEnumerable<myNamespace.DTO.UserDTO>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.)
---- System.InvalidOperationException : The source IQueryable doesn't implement IAsyncEnumerable<myNamespace.DTO.UserDTO>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
这是我的单元测试:
//Arrange
var mockSet = Substitute.For<DbSet<User>, IQueryable<User>, IDbAsyncEnumerable<User>>();
((IDbAsyncEnumerable<User>)mockSet).GetAsyncEnumerator()
.Returns(new TestDbAsyncEnumerator<User>(GetUserList().AsQueryable().GetEnumerator()));
((IQueryable<User>)mockSet).Provider.Returns(new TestDbAsyncQueryProvider<User>(GetUserList().AsQueryable().Provider));
((IQueryable<User>)mockSet).Expression.Returns(GetUserList().AsQueryable().Expression);
((IQueryable<User>)mockSet).ElementType.Returns(GetUserList().AsQueryable().ElementType);
((IQueryable<User>)mockSet).GetEnumerator().Returns(GetUserList().AsQueryable().GetEnumerator());
var mockContext = Substitute.For<IMyContext>();
mockContext.Users.Returns(mockSet);
//Act
CancellationToken cancellationToken = new CancellationToken();
UserRepository userRepository = new UserRepository(mockContext);
var users = userRepository.GetListAsync(cancellationToken).Result;
//Assert
Assert.NotNull(users);
我想测试我的仓库:
public async Task<IEnumerable<UserDto>> GetListAsync(CancellationToken cancellationToken)
{
return await _myContext.Users.Select(u => new UserDto
{
Id = u.Id,
FistName = u.FistName ,
LastName = u.LastName
}).ToListAsync(cancellationToken);
}
有什么问题?
正如 OP 评论中所述,您所指的 doco 适用于 EF,而不是 EFCore。您需要实现一组不同的接口。
通常的建议是避免模拟 DbContext
但是在这种情况下,您可能需要这样做,因为内存提供程序不支持异步操作。我不确定 SQLite 是否支持它们。 EntityFrameworkCore.Testing 应该处理这种情况(免责声明,我是作者),但您需要使用上下文实现而不是接口。
实现此功能的最常见方法是创建异步接口的实现,其方式与 EF doco 相同,但适用于 EFCore。您会发现大多数 EFCore 模拟库都会这样做:
public class TestAsyncEnumerable<T> : IAsyncEnumerable<T>, IOrderedQueryable<T>
{
private readonly IEnumerable<T> _enumerable;
private readonly IQueryable<T> _queryable;
public TestAsyncEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
_queryable = _enumerable.AsQueryable();
ElementType = _queryable.ElementType;
Expression = _queryable.Expression;
Provider = new TestAsyncQueryProvider<T>(_queryable);
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return new TestAsyncEnumerator<T>(_queryable);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public TestAsyncEnumerator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
public TestAsyncQueryProvider(IQueryable<T> source)
{
Source = source;
}
private IQueryable<T> Source { get; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken())
{
throw new NotImplementedException();
}
}
这不是一个完整的实现,只是解决 OP 案例所需要的。重要的一点是这一行:
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
这将允许投影与异步操作一起工作。
工作示例:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using AutoFixture;
using KellermanSoftware.CompareNetObjects;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Internal;
using NSubstitute;
using NUnit.Framework;
namespace Question62783423
{
public class Tests
{
[Test]
public void Test1()
{
var fixture = new Fixture();
var users = new TestAsyncEnumerable<User>(fixture.CreateMany<User>());
//Arrange
var mockSet = Substitute.For<DbSet<User>, IQueryable<User>, IAsyncEnumerable<User>>();
((IAsyncEnumerable<User>) mockSet).GetAsyncEnumerator().Returns(users.GetAsyncEnumerator());
((IQueryable<User>) mockSet).Provider.Returns(users.Provider);
((IQueryable<User>) mockSet).Expression.Returns(users.Expression);
((IQueryable<User>) mockSet).ElementType.Returns(users.ElementType);
((IQueryable<User>) mockSet).GetEnumerator().Returns(((IQueryable<User>) users).GetEnumerator());
var mockContext = Substitute.For<IMyContext>();
mockContext.Users.Returns(mockSet);
//Act
var cancellationToken = new CancellationToken();
var userRepository = new UserRepository(mockContext);
var result1 = userRepository.GetListAsync(cancellationToken).Result;
var result2 = userRepository.GetListAsync(cancellationToken).Result;
var comparer = new CompareLogic();
comparer.Config.IgnoreCollectionOrder = true;
comparer.Config.IgnoreObjectTypes = true;
var comparisonResult1 = comparer.Compare(users, result1);
var comparisonResult2 = comparer.Compare(users, result2);
Assert.That(comparisonResult1.Differences.Any(), Is.False);
Assert.That(comparisonResult2.Differences.Any(), Is.False);
}
}
}
public class TestAsyncEnumerable<T> : IAsyncEnumerable<T>, IOrderedQueryable<T>
{
private readonly IEnumerable<T> _enumerable;
private readonly IQueryable<T> _queryable;
public TestAsyncEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
_queryable = _enumerable.AsQueryable();
ElementType = _queryable.ElementType;
Expression = _queryable.Expression;
Provider = new TestAsyncQueryProvider<T>(_queryable);
}
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return new TestAsyncEnumerator<T>(_queryable);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerable.GetEnumerator();
}
public Type ElementType { get; }
public Expression Expression { get; }
public IQueryProvider Provider { get; }
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public TestAsyncEnumerator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
}
public ValueTask DisposeAsync()
{
return new ValueTask();
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
public TestAsyncQueryProvider(IQueryable<T> source)
{
Source = source;
}
private IQueryable<T> Source { get; }
public IQueryable CreateQuery(Expression expression)
{
throw new NotImplementedException();
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(Source.Provider.CreateQuery<TElement>(expression));
}
public object Execute(Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new CancellationToken())
{
throw new NotImplementedException();
}
}
public class User
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserDto
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IMyContext
{
DbSet<User> Users { get; set; }
}
public class UserRepository
{
private readonly IMyContext _myContext;
public UserRepository(IMyContext myContext)
{
_myContext = myContext;
}
public async Task<IEnumerable<UserDto>> GetListAsync(CancellationToken cancellationToken)
{
return await _myContext.Users.Select(u => new UserDto { Id = u.Id, FirstName = u.FirstName, LastName = u.LastName }).ToListAsync(cancellationToken);
}
}