2012-02-01 111 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使用。

相關問題