[英]Unit Testing Controller with Authorization ASP.NET Core 2.0 Web API
I have a controller: 我有一个控制器:
public class InvitationsController: Controller {
private readonly IMapper _mapper;
private readonly IInvitationManager _invitationManager;
private readonly UserManager<MyAppUser> _userManager;
public InvitationsController(
IInvitationManager invitationManager,
IMapper mapper,
UserManager<MyAppUser> userManager,
IJobManager jobManager
) {
_invitationManager = invitationManager;
_mapper = mapper;
_userManager = userManager;
}
[Authorization]
GetInvitationByCode(string code) { ... }
I'm trying to write unit tests using Xunit and Moq. 我正在尝试使用Xunit和Moq编写单元测试。 Here is the implentation of my test: 这是我的测试的实现:
public class InvitationsControllerTests {
private Mock<IInvitationManager> invitationManagerMock;
private Mock<UserManager<MyAppUser>> userManagerMock;
private Mock<IMapper> mapperMock;
private InvitationsController controller;
public InvitationsControllerTests() {
invitationManagerMock = new Mock<IInvitationManager>();
userManagerMock = new Mock<UserManager<MyAppUser>>();
mapperMock = new Mock<IMapper>();
controller = new InvitationsController(invitationManagerMock.Object,
mapperMock.Object,
userManagerMock.Object);
}
[Fact]
public async Task GetInvitationByCode_ReturnsInvitation() {
var mockInvitation = new Invitation {
StoreId = 1,
InviteCode = "123abc",
};
invitationManagerMock.Setup(repo =>
repo.GetInvitationByCodeAsync("123abc"))
.Returns(Task.FromResult(mockInvitation));
var result = await controller.GetInvitationByCode("123abc");
Assert.Equal(mockInvitation, result);
}
I don't think I'm using the mocking functionality correctly. 我认为我没有正确使用模拟功能。 Specifically with UserManager. 特别是使用UserManager。 I can't find a clear answer on using Moq to test controllers protected by [Authorize]. 我无法找到使用Moq测试受[Authorize]保护的控制器的明确答案。 When running my tests, it throws an exception on 运行我的测试时,它会抛出异常
controller = new InvitationsController(invitationManagerMock.Object,
mapperMock.Object,
userManagerMock.Object);
Which reads: 其中包括:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: 'Can not instantiate proxy of class: Microsoft.AspNetCore.Identity.UserManager`1[[MyApp.api.Core.Models.MyAppUser, MyApp.api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. Castle.DynamicProxy.InvalidProxyConstructorArgumentsException:'无法实例化类的代理:Microsoft.AspNetCore.Identity.UserManager`1 [[MyApp.api.Core.Models.MyAppUser,MyApp.api,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = NULL]]。 Could not find a parameterless constructor.' 找不到无参数构造函数。'
You're not unit testing; 你不是单元测试; you're integration testing. 你是集成测试。 When you find yourself setting up ten thousand mocks just to run a method, that's a pretty good sign it's an integration test. 当你发现自己设置了一万个模拟只是为了运行一个方法时,这是一个非常好的迹象,它是一个集成测试。 Additionally, things like authorization only happen as part of the request lifecycle; 此外,授权等事项仅作为请求生命周期的一部分发生; there's no way to test that, without doing an actual request, which again, means you're integration testing. 在没有做出实际请求的情况下,没有办法测试它,这再次意味着你进行了集成测试。
As such, use the test host . 因此,请使用测试主机 。
private readonly TestServer _server;
private readonly HttpClient _client;
public MyTestClass()
{
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task GetInvitationByCode_ReturnsInvitation() {
var mockInvitation = new Invitation {
StoreId = 1,
InviteCode = "123abc",
};
var response = await _client.GetAsync("/route");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Invitation>(responseString);
// Compare individual properties you care about.
// Comparing the full objects will fail because of reference inequality
Assert.Equal(mockInvitation.StoreId, result.StoreId);
}
If you need to scaffold your data to make the correct result return, simply use the in-memory database provider . 如果您需要为数据构建工具以返回正确的结果,只需使用内存数据库提供程序即可 。 The easiest way to use this for integration testing is to specify a new environment like "Test". 使用它进行集成测试的最简单方法是指定一个像“Test”这样的新环境。 Then, in your startup, when configuring your context, branch on the environment and use the in-memory provider (instead of SQL Server or whatever) when the environment is "Test". 然后,在启动时,在配置上下文时,分支环境并在环境为“Test”时使用内存提供程序(而不是SQL Server或其他)。 Then, when setting up your test server for integration testing, simply add .UseEnvironment("Test")
before .UseStartup<Startup>()
. 然后,在设置测试服务器进行集成测试时,只需在.UseStartup<Startup>()
之前添加.UseEnvironment("Test")
.UseStartup<Startup>()
。
I think, problem is in dependency injection. 我认为,问题在于依赖注入。 In your Startups.cs file you could find similar string: services.AddIdentity<AppUser, AppRole>().AddEntityFrameworkStores<AppDbContext>().AddDefaultTokenProviders();
在您的Startups.cs文件中,您可以找到类似的字符串: services.AddIdentity<AppUser, AppRole>().AddEntityFrameworkStores<AppDbContext>().AddDefaultTokenProviders();
it means that magic of namespace Microsoft.Extensions.DependencyInjection
provide you an instance of your User- or RoleManger anywhere where you want to use it. 它意味着名称空间Microsoft.Extensions.DependencyInjection
魔力为您提供了一个用户或RoleManger实例,您可以在任何地方使用它。 For example, in InvitationsController using injectin in constructor. 例如,在构造函数中使用injectin的InvitationsController中。
You can try inject UserManger in test class and mock it. 您可以尝试在测试类中注入UserManger并进行模拟。 Or read similar question 或者阅读类似的问题
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.