我正在我的.NET Core Web应用程序中使用测试我的个人信息动作,但一直遇到问题。我最近找到了一种方法来创建一个模拟的usermanager,而不会在它的参数上遇到问题,但随后我又遇到了一个新的错误,我也找不到任何解决方案。"System.TypeLoadException: 无法加载类型'Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider'"
这是我的相关代码。
设置模拟usermanager:
var _userManager = new Mock<FakeUserManager>();
UserIdentity user1 = new UserIdentity() { Id = UserId1, UserName = "[email protected]", Score = 5 };
UserIdentity user2 = new UserIdentity() { Id = UserId2, UserName = "[email protected]", Score = 1 };
UserIdentity user3 = new UserIdentity() { Id = UserId3, UserName = "[email protected]", Score = 0 };
UserIdentity user4 = new UserIdentity() { Id = UserId4, UserName = "[email protected]", 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("[email protected]", 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 = "[email protected]", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "[email protected]", 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 = "[email protected]", Score = 1 };
var user2 = new UserIdentity() { Id = Guid.NewGuid(), UserName = "[email protected]", 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
而不是模拟;如果你想走这条路,请按照链接看一个例子。