C# 具有授权ASP.NET Core 2.0 Web API的单元测试控制器
我有一个控制器:C# 具有授权ASP.NET Core 2.0 Web API的单元测试控制器,c#,unit-testing,asp.net-core,moq,C#,Unit Testing,Asp.net Core,Moq,我有一个控制器: public class InvitationsController: Controller { private readonly IMapper _mapper; private readonly IInvitationManager _invitationManager; private readonly UserManager<MyAppUser> _userManager; public In
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) { ... }
内容如下:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException:“无法实例化类的代理:Microsoft.AspNetCore.Identity.UserManager`1[[MyApp.api.Core.Models.MyAppUser,MyApp.api,版本=1.0.0.0,区域性=中性,PublicKeyToken=null]]。
找不到无参数构造函数。“我想,问题在于依赖项注入。在Startups.cs文件中,您可以找到类似的字符串:
services.AddIdentity您不是单元测试;您正在进行集成测试。当你发现自己设置了一万个mock只是为了运行一个方法时,这是一个很好的迹象,这是一个集成测试。此外,授权之类的事情只在请求生命周期中发生;如果不执行实际的请求,就无法进行测试,这同样意味着您正在进行集成测试
因此,请使用
private readonly TestServer\u服务器;
私有只读HttpClient\u客户端;
公共MyTestClass()
{
_服务器=新的TestServer(新的WebHostBuilder()
.UseStartup());
_client=_server.CreateClient();
}
[事实]
公共异步任务GetInvitationByCode_ReturnsInvitation(){
var mockInvitation=新邀请{
StoreId=1,
InviteCode=“123abc”,
};
var response=wait_client.GetAsync(“/route”);
response.EnsureSuccessStatusCode();
var responseString=await response.Content.ReadAsStringAsync();
var result=JsonConvert.DeserializeObject(responseString);
//比较您关心的各个属性。
//由于引用不平等,比较完整对象将失败
Assert.Equal(mockInvitation.StoreId,result.StoreId);
}
如果需要构建数据以返回正确的结果,只需使用。将其用于集成测试的最简单方法是指定一个新环境,如“Test”。然后,在启动时,在配置上下文时,在环境为“测试”时,在环境上进行分支,并使用内存中的提供程序(而不是SQL Server或其他什么)。然后,在为集成测试设置测试服务器时,只需在之前添加.UseEnvironment(“test”)
。UseStartup()
UserManager
是一个应该抽象出来的实现问题。将使模拟和注入更容易。在测试控制器类时,您将无法测试AuthorizationAttribute
的实际工作。因为中间件使用了AuthorizationAttribute
。对于需要测试完整管道的测试:创建客户端(HttpClient
),启动WebAPI,将请求发送到正在测试的url,并在响应时断言。在测试控制器及其方法时,您是否推荐集成测试方法而不是单元测试?控制器操作,是。如果你的控制器上有某种实用方法,而不是一个动作,你可以对它进行单元测试。行动本质上是整合。除非你通过请求点击它,否则你不会知道它是否真的在工作。这个答案确实让我走上了正确的轨道,但是你如何“伪造”经过身份验证的用户,以便你可以运行一个显示授权通过的测试?ie如何测试应用的授权属性?ie[Authorize(Policy=“Is18orOlder”)]或任何策略?将一个或多个测试用户插入内存数据库。然后,使用测试客户端点击login post操作,并为要测试的用户发送用户名和密码。响应将包含您的身份验证cookie。使用它再次通过测试客户端发出经过身份验证的请求。根据被测试的用户,断言授权相应地通过或失败。
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);
}
controller = new InvitationsController(invitationManagerMock.Object,
mapperMock.Object,
userManagerMock.Object);
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);
}