单元测试自定义MembershipProvider.ValidateUser,使用Global.asax中的代码

时间:2012-02-01 13:50:24

标签: asp.net-mvc-3 unit-testing ninject moq

我有一个单元测试来检查我的AccountController.LogIn方法。返回重定向结果以指示成功,否则返回viewresult。

测试总是失败,因为返回类型始终是viewresult,即使测试应该在凭据有效时返回成功,但是我无法确定问题所在。 我的TestMethod: 的 CustomerRepositoryTest.cs

[TestMethod]
public void Can_Login_With_Valid_Credentials()
{
     //  Arrange
     Mock<IAddressRepository> mockAddressRepository = new Mock<IAddressRepository>();
     Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>();
     Mock<IOrderRepository> mockOrderRepository = new Mock<IOrderRepository>();

     LoginViewModel model = new LoginViewModel
     {
         Email = "me@5.com",
         Password = "password"
     };

     AccountController target = new AccountController(mockCustomerRepository.Object, mockAddressRepository.Object, mockOrderRepository.Object);

   //  Act
    ActionResult result = target.LogIn(model);

    //  Assert
    Assert.IsInstanceOfType(result, typeof(RedirectResult));
    Assert.AreEqual("", ((RedirectResult)result).Url);
    }

当我运行测试时,当我调用ValidateUser时,它在My AccountController登录方法中失败 的 AccountController.cs

if (Membership.ValidateUser(LoginModel.Email, LoginModel.Password))
{
  ...
 return RedirectToRoute(new
 {
    controller = "Account",
    action = "Details"
 });
}
else
{
   return View();
}

我的自定义MembershipProvider ValidateUser如下所示:

AccountMembershipProvider.cs

public class AccountMembershipProvider : MembershipProvider
{
     [Inject]
    public ICustomerRepository repository { get; set; }

    public override bool ValidateUser(string username, string password)
    {
       var cust = repository.GetAllCustomers().SingleOrDefault..

当我正常运行应用程序,即没有测试时,登录正常。在应用程序中,我将CustomerRepository注入 Global.asax 中的自定义成员资格提供程序:

    public class MvcApplication : System.Web.HttpApplication
    {
      private IKernel _kernel = new StandardKernel(new MyNinjectModules());

      internal class MyNinjectModules : NinjectModule
      {
        public override void Load()
        {
            Bind<ICustomerRepository>().To<CustomerRepository>();
        }
      }

      protected void Application_Start()

  {
   _kernel.Inject(Membership.Provider);
   ...

是否在单元测试时没有运行Global.asax代码?所以我的自定义提供程序没有被注入,因此失败了吗?

更新 我模拟了我的Provider类,并将模拟的CustomerRepository对象传递给它。

Mock<AccountMembershipProvider> provider = new Mock<AccountMembershipProvider>();

provider.Object.repository = mockCustomerRepository.Object;

然后我为我正在尝试测试的方法创建了一个设置:

 mockCustomerRepository.Setup(m => m.IsValidLogin("me@5.com", "password")).Returns(true);

但不幸的是,我每次都会失败。要回答关于我是否需要一个真实或模拟对象进行测试的问题 - 我不挑剔,我只想让它在此刻工作!

更新2

我做了这些更改,虽然它仍然失败,但它让我能够确定具体问题。在调试测试时,我发现当我调用被覆盖的

Membership.ValidateUser(LoginModel.Email, LoginModel.Password)

Membership.Provider的类型为SqlMembershipProvider(可能是默认类型),因此验证失败。

如果我将提供商转发给我的自定义提供商......

((AccountMembershipProvider)Membership.Provider).ValidateUser(LoginModel.Email, LoginModel.Password)

运行测试时出现InvalidCastException。因此,我的模拟AccountMembershipProvider似乎没有用于测试,而是使用默认提供程序。

我认为您已经在评论中确定了这一点:

// set your mock provider in your AccountController

但是我不确定你到底是什么意思 - 我的AccountController上没有属性来分配提供者,我不会将它注入构造函数。

2 个答案:

答案 0 :(得分:3)

您的原始问题: “是不是在单元测试时没有运行Global.asax代码?所以我的自定义提供程序没有被注入,因此失败?” < / p>

我的回答:

在运行时,ASP.Net和ISS使用global.asax文件。它在服务器收到第一个请求时编译。

当您进行测试时,您不在运行在ISS中的ASP.Net Web应用程序的上下文中,而是在测试会话中运行的程序中。这意味着你的global.asax不会被调用。

更重要的是,当你致电:

Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>();

Ninject不会被称为填充导入。 Moq将根据界面创建一个模拟器。不会创建任何真实对象。

由于您没有定义任何安装方法,即:

mockCustomerRepository.Setup(mock => mock.MyAuthenticateMethod()).Returns(true);

你正在传递没有定义行为的模拟。 默认情况下,这些将返回false。这可能解释了为什么你总是得到一个viewresult。

您需要做的是为您需要模拟的方法定义设置方法。

这些是CustomerRepository的方法,在调用时会调用你:

target.LogIn(model);

另请注意,由于不会使用NInject,因此您的AccountMembershipProvider将无法注入CustomerRepository。如果您正在测试AccountController并且它不是静态的(Moq不能用于静态),您应该考虑模拟AccountMembershipProvider。如果你不能,那么你需要在测试中将CustomerRepository的模拟实例提供给AccountMembershipProvider.repository。

另一种解决方案,您也可以在测试中手动创建(使用新的)CustomerRepository的真实实例,而不是创建Moq模拟。

你可以让Ninject这样做,但此时为什么呢?您可以自己创建它,并且知道要创建的特定类型。

如果您需要此测试的模拟或实际对象实例,那么这一切都归结为。

<强>更新

如果您的提供程序被模拟,则无需在其上设置存储库。当您调用mock时,不会调用真实对象。

你需要做的是这样的事情:

Mock<AccountMembershipProvider> provider = new Mock<AccountMembershipProvider>();
// set your mock provider in your AccountController

provider.Setup(m => m.ValidateUser("me@5.com", "password")).Returns(true);

更新2:

我认为你非常接近于让你的测试工作。没有看到你的所有代码(测试和测试中的类)我真的不能给你更多的帮助。我也觉得我回答了你原来的问题,但是如果你仍然陷入困境,你可以通过询问一个与你正在解决的问题相关的新问题来获得更多的帮助。

答案 1 :(得分:1)

在您的代码中,您只创建控制器,但不会为成员资格运行初始化。我建议您使用方法ValidateUser创建自己的UserService,而不是静态类成员资格使用。