2012-02-01 64 views
2

我有一个单元测试来检查我的AccountController.LogIn方法。返回重定向结果以指示成功,否则返回viewresult。单元测试自定义MembershipProvider.ValidateUser,使用Global.asax中的代码

测试总是失败,因为返回类型始终是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 = "[email protected]", 
     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); 
    } 

当我运行测试,它在我的AccountController登录方法失败,当我打电话的ValidateUser 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代码不同时,单元测试运行的情况下?所以我的自定义提供程序没有被注入,因此失败?

UPDATE 我嘲笑我的Provider类,并将模拟的CustomerRepository对象传递给它。

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

provider.Object.repository = mockCustomerRepository.Object; 

然后我创建了一个针对设置方法我想测试:

mockCustomerRepository.Setup(m => m.IsValidLogin("[email protected]", "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一个属性来指定供应商,并我没有注入到构造函数中。

+0

为了让您知道,我在一天的早些时候通过编辑添加了更多信息。不要犹豫,要求澄清或更具体的信息。 – Gilles 2012-02-02 00:24:12

+0

@Gilles - 我实施了你的建议(见更新)。他们为我澄清了很多,但每次都不幸失败!我是否需要为Provider和Repository类定义设置方法? – markpsmith 2012-02-02 11:27:56

+0

我用一个例子更新我的帖子..基本上,如果你的提供者被嘲笑,没有必要为它的依赖创建模拟,因为模拟对象不会调用它们。 – Gilles 2012-02-02 13:41:20

回答

3

你原来的问题: “难道在Global.asax代码不同时,单元测试运行,所以我的自定义提供不被注入,因此失败的情况?”

我的回答:

是。

Global.asax文件是在运行时使用的ASP.Net和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); 

另外请注意,您的AccountMembershipProvider不会得到它的CustomerRepository注入因为NInject将不会被使用。如果你正在测试AccountController并且它不是静态的(Moq不能用于静态),你应该考虑嘲笑AccountMembershipProvider。如果不能,那么你就需要CustomerRepository你嘲笑实例提供给AccountMembershipProvider.repository在你的测试。

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

你可以有Ninject做到这一点,但在这一点上,为什么?您可以自己创建它,并且知道要创建的具体类型。

这一切都归结为如果你需要一个模拟对象或这个测试的实例。

更新:

如果您的提供商嘲笑,没有必要设置库就可以了。当你调用模拟器时,真正的对象不会被调用。

你需要做的是这样的:

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

provider.Setup(m => m.ValidateUser("[email protected]", "password")).Returns(true); 

更新2:

我觉得你已经很接近让您的测试工作。没有看到你所有的代码(测试和测试类),我真的不能给你任何帮助。我也觉得我回答了你原来的问题,但如果你仍然陷入困境,你可能会通过询问一个与你正在处理的问题有关的新问题来获得更多帮助。

+0

在我最后的编辑中,我在答案中添加了更多信息。新的部分用斜体表示。 – Gilles 2012-02-01 19:12:47

+0

我觉得我原来的问题确实得到了回答,而且你超越了简单提供答案的事实是值得你信任的。 – markpsmith 2012-02-06 09:34:36

+0

@Gilles,你说过“//在你的AccountController中设置你的模拟提供者”,你可以给我一个如何设置提供者到控制器的例子 – 2015-01-18 10:10:17

1

在你的代码只能创建控制器,而不是成员初始化运行。我建议你使用ValidateUser方法创建自己的UserService,而不是静态类Membership使用。