unit-testing - System.TypeLoadException:无法加载类型“Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider”
问题描述
我正在我的 .NET Core Web 应用程序中测试我的身份操作,但一直遇到问题。我最近找到了一种创建模拟用户管理器而不会遇到其参数问题的方法,但随后出现一个新错误,我也找不到任何解决方案:“System.TypeLoadException:无法加载类型'Microsoft.EntityFrameworkCore。 Query.Internal.IAsyncQueryProvider'"
这是我的相关代码:
设置模拟用户管理器:
var _userManager = new Mock<FakeUserManager>();
UserIdentity user1 = new UserIdentity() { Id = UserId1, UserName = "test@gmail.com", Score = 5 };
UserIdentity user2 = new UserIdentity() { Id = UserId2, UserName = "pragim@gmail.com", Score = 1 };
UserIdentity user3 = new UserIdentity() { Id = UserId3, UserName = "ajax@gmail.com", Score = 0 };
UserIdentity user4 = new UserIdentity() { Id = UserId4, UserName = "pim@gmail.com", Score = 4 };
List<UserIdentity> users = new List<UserIdentity>() { user1, user2, user3, user4 };
var mock = users.AsQueryable().BuildMock();
_userManager.Setup(x => x.Users).Returns(mock.Object);
var identityRepository = new IdentityRepository(_userManager.Object, null, null);
_identityService = new IdentityService(identityRepository);
FakeUserManager.cs:
public class FakeUserManager : UserManager<UserIdentity>
{
public FakeUserManager()
: base(new Mock<IUserStore<UserIdentity>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<UserIdentity>>().Object,
new IUserValidator<UserIdentity>[0],
new IPasswordValidator<UserIdentity>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<UserIdentity>>>().Object)
{ }
}
测试方法:
[TestMethod()]
public async Task GetUserAsyncTest()
{
//Arrange
//Act
var user = await _identityService.GetUserAsync(UserId4);
//Assert
Assert.AreEqual("pim@gmail.com", user.UserName);
}
有谁知道我的问题的绕过/解决方案?
更新:堆栈跟踪:
EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
EntityFrameworkQueryableExtensions.SingleOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
IdentityRepository.GetUserAsync(Nullable`1 userId) line 55
IdentityService.GetUserAsync(Nullable`1 id) line 172
IdentityServiceTests.GetUserAsyncTest() line 102
ThreadOperations.ExecuteWithAbortSafety(Action action)
以及存储库中的 GetUserAsync 方法:
public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
return await _userManager.Users.Where(x => x.Id.Equals(userId)).SingleOrDefaultAsync();
}
解决方案
该_users
序列需要一个实现IAsyncQueryProvider
. 有几种方法可以做到这一点,以下是一个模拟的实现。
首先,一些基于您的 OP 的脚手架:
public class UserIdentity : IdentityUser<Guid>
{
public int Score { get; set; }
}
public class IdentityRepository
{
readonly UserManager<UserIdentity> _userManager;
public IdentityRepository(UserManager<UserIdentity> userManager)
{
_userManager = userManager;
}
public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
return await _userManager.Users.SingleOrDefaultAsync(x => x.Id.Equals(userId));
}
}
创建您的模拟IAsyncQueryProvider
并将其添加到您自己的IQueryable<UserIdentity>
:
var user1 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "foo@bar.com", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "bar@baz.com", Score = 2 };
var user3 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "tony stark", Score = 3000 };
var dataSource = new List<UserIdentity> { user1, user2, user3 }.AsQueryable();
var providerMock = new Mock<IAsyncQueryProvider>();
providerMock.Setup(x => x.ExecuteAsync<Task<UserIdentity>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.Returns((Expression providedExpression, CancellationToken providedCancellationToken) => Task.FromResult(dataSource.Provider.Execute<UserIdentity>(providedExpression)));
var usersMock = new Mock<IQueryable<UserIdentity>>();
usersMock.Setup(x => x.ElementType).Returns(dataSource.ElementType);
usersMock.Setup(x => x.Expression).Returns(dataSource.Expression);
usersMock.Setup(x => x.Provider).Returns(providerMock.Object);
...
userManagerMock.Setup(x => x.Users).Returns(() => usersMock.Object);
您需要一种设置IQueryable<T>
提供程序的方法,以上只是您可以做到的一种方法。
如果我们将所有这些整合到一个有效的 LINQPad 示例中:
void Main()
{
var user1 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "foo@bar.com", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "bar@baz.com", Score = 2 };
var user3 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "tony stark", Score = 3000 };
var dataSource = new List<UserIdentity> { user1, user2, user3 }.AsQueryable();
var providerMock = new Mock<IAsyncQueryProvider>();
providerMock.Setup(x => x.ExecuteAsync<Task<UserIdentity>>(It.IsAny<Expression>(), It.IsAny<CancellationToken>()))
.Returns((Expression providedExpression, CancellationToken providedCancellationToken) => Task.FromResult(dataSource.Provider.Execute<UserIdentity>(providedExpression)));
var usersMock = new Mock<IQueryable<UserIdentity>>();
usersMock.Setup(x => x.ElementType).Returns(dataSource.ElementType);
usersMock.Setup(x => x.Expression).Returns(dataSource.Expression);
usersMock.Setup(x => x.Provider).Returns(providerMock.Object);
var userManagerMock = new Mock<UserManager<UserIdentity>>
(new Mock<IUserStore<UserIdentity>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<UserIdentity>>().Object,
new IUserValidator<UserIdentity>[0],
new IPasswordValidator<UserIdentity>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<UserIdentity>>>().Object);
userManagerMock.Setup(x => x.Users).Returns(() => usersMock.Object);
var identityRepository = new IdentityRepository(userManagerMock.Object);
var result = identityRepository.GetUserAsync(user2.Id).Result;
Console.WriteLine(result);
}
public class UserIdentity : IdentityUser<Guid>
{
public int Score { get; set; }
}
public class IdentityRepository
{
readonly UserManager<UserIdentity> _userManager;
public IdentityRepository(UserManager<UserIdentity> userManager)
{
_userManager = userManager;
}
public async Task<UserIdentity> GetUserAsync(Guid? userId)
{
return await _userManager.Users.SingleOrDefaultAsync(x => x.Id.Equals(userId));
}
}
我们得到了想要的结果:
我IdentityService
在上面省略了你的,因为它本质上是答案的细节;你只需要得到一个工作,嘲笑UserManager
。对于我的模拟库,特别是EntityFrameworkCore.Testing,我会使用具体IAsyncQueryProvider
而不是模拟;如果您想走这条路,请点击链接获取示例。
推荐阅读
- mongodb - 连接到 MongoDB 的最佳方式
- python - 使用 python 从 .tar.gz 文件中仅提取 jpg 文件
- c# - 与 ASP.NET Core 选项服务绑定时忽略模型属性
- scroll - 使用钩子检测滚动方向
- c++ - 为什么这两个数字比较相等?
- server - 微软 BizTalk HIS SNA
- android - ShapeableImageView 不显示矢量图像
- flutter - (Dart) 小部件不是一种类型
- javascript - Reactjs:为什么我的 Android 虚拟设备显示“this.setState_if_Tie”不是一个函数?
- ruby-on-rails - 无法在 Rails 中销毁会话/注销