gpt4 book ai didi

c# - 单元测试 ASP.NET MVC5 应用程序

转载 作者:IT王子 更新时间:2023-10-29 04:16:48 25 4
gpt4 key购买 nike

我通过添加一个新属性来扩展 ApplicationUser 类(如教程中所示 Create an ASP.NET MVC 5 App with Facebook and Google OAuth2 and OpenID Sign-on (C#) )

public class ApplicationUser : IdentityUser
{
public DateTime BirthDate { get; set; }
}

现在我想创建一个单元测试来验证我的 AccountController 是否正确保存了出生日期。

我创建了一个名为 TestUserStore 的内存中用户存储

[TestMethod]
public void Register()
{
// Arrange
var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
var controller = new AccountController(userManager);

// This will setup a fake HttpContext using Moq
controller.SetFakeControllerContext();

// Act
var result =
controller.Register(new RegisterViewModel
{
BirthDate = TestBirthDate,
UserName = TestUser,
Password = TestUserPassword,
ConfirmPassword = TestUserPassword
}).Result;

// Assert
Assert.IsNotNull(result);

var addedUser = userManager.FindByName(TestUser);
Assert.IsNotNull(addedUser);
Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}

controller.Register 方法是由 MVC5 生成的样板代码,但出于引用目的,我将其包含在此处。

// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName, BirthDate = model.BirthDate };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}

// If we got this far, something failed, redisplay form
return View(model);
}

当我调用 Register 时,它会调用 SignInAsync,这就是问题所在。

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

在最低层,样板代码包含这个花絮

private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}

这就是问题的根源所在。对 GetOwinContext 的调用是一种扩展方法,我无法模拟,也无法用 stub 替换(当然,除非我更改样板代码)。

当我运行这个测试时我得到一个异常

Test method MVCLabMigration.Tests.Controllers.AccountControllerTest.Register threw exception: 
System.AggregateException: One or more errors occurred. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.HttpContextBaseExtensions.GetOwinEnvironment(HttpContextBase context)
at System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase context)
at MVCLabMigration.Controllers.AccountController.get_AuthenticationManager() in AccountController.cs: line 330
at MVCLabMigration.Controllers.AccountController.<SignInAsync>d__40.MoveNext() in AccountController.cs: line 336

在以前的版本中,ASP.NET MVC 团队非常努力地使代码可测试。从表面上看,现在测试 AccountController 并不容易。我有一些选择。

我可以

  1. 修改样板代码,使其不调用扩展方法并在该级别处理此问题

  2. 为测试目的设置 OWin 管道

  3. 避免编写需要 AuthN/AuthZ 基础设施的测试代码(不是一个合理的选择)

我不确定哪条路更好。任何一个都可以解决这个问题。我的问题归结为最佳策略。

注意:是的,我知道我不需要测试不是我写的代码。 MVC5 提供的 UserManager 基础设施就是这样一个基础设施,但是如果我想编写测试来验证我对 ApplicationUser 的修改或代码来验证依赖于用户角色的行为,那么我必须使用 UserManager 进行测试。

最佳答案

我正在回答我自己的问题,因此如果您认为这是一个很好的答案,我可以从社区中了解一下。

第 1 步:修改生成的 AccountController 以使用支持字段为 AuthenticationManager 提供属性 setter 。

// Add this private variable
private IAuthenticationManager _authnManager;

// Modified this from private to public and add the setter
public IAuthenticationManager AuthenticationManager
{
get
{
if (_authnManager == null)
_authnManager = HttpContext.GetOwinContext().Authentication;
return _authnManager;
}
set { _authnManager = value; }
}

第 2 步:修改单元测试以添加 Microsoft.OWin.IAuthenticationManager 接口(interface)的模拟

[TestMethod]
public void Register()
{
// Arrange
var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
var controller = new AccountController(userManager);
controller.SetFakeControllerContext();

// Modify the test to setup a mock IAuthenticationManager
var mockAuthenticationManager = new Mock<IAuthenticationManager>();
mockAuthenticationManager.Setup(am => am.SignOut());
mockAuthenticationManager.Setup(am => am.SignIn());

// Add it to the controller - this is why you have to make a public setter
controller.AuthenticationManager = mockAuthenticationManager.Object;

// Act
var result =
controller.Register(new RegisterViewModel
{
BirthDate = TestBirthDate,
UserName = TestUser,
Password = TestUserPassword,
ConfirmPassword = TestUserPassword
}).Result;

// Assert
Assert.IsNotNull(result);

var addedUser = userManager.FindByName(TestUser);
Assert.IsNotNull(addedUser);
Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}

现在测试通过了。

好主意?坏主意?

关于c# - 单元测试 ASP.NET MVC5 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19624140/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com