2016-03-01 165 views
2

我想單元測試一些依賴於UserManagerRoleManager的方法,並且遇到了一些困難。單元測試依賴於UserManager和RoleManager

我應該嘲笑UserManagerRoleManager,然後將它傳遞給AdminController?或者我應該首先訪問AccountController的默認SignIn操作並進行身份驗證。我不確定如何做這兩種選擇或什麼是最好的方法來解決這個問題。

當不認證/實例我上UserManager

我的測試NullReferenceException異常

[Test] 
    public void MaxRole_SuperAdmin() 
    { 
     var adminController = new AdminController(); 
     var maxRole = adminController.GetMaxRole(SuperAdminUserId); 

     Assert.AreEqual(maxRole, "Super Admin"); 
    } 

控制器和控制方法

[Authorize(Roles = "APGame Admin, APGame Investigator")] 
[RequireHttps] 
public class AdminController : Controller 
{ 

    private ApplicationUserManager _userManager; 

    public ApplicationUserManager UserManager 
    { 
     get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); } 
     private set { _userManager = value; } 
    } 

    private ApplicationRoleManager roleManager; 

    public ApplicationRoleManager RoleManager 
    { 
     get 
     { 
      return roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>(); 
     } 
     private set { roleManager = value; } 
    } 

    public string GetMaxRole(string userId) 
    { 
     IEnumerable<string> userRoles = UserManager.GetRoles(userId); 

     string role = null; 

     if (userRoles.Contains("APGame Admin")) 
     { 
      if (userRoles.Contains("TeVelde Group") && userRoles.Contains("Genomics Group")) 
       role = "Super Admin"; 

      else role = "Admin"; 
     } 

     else if (userRoles.Contains("APGame Investigator")) 
     { 
      role = "Investigator"; 
     } 

     else if (userRoles.Contains("APGame User")) 
     { 
      role = "User"; 
     } 

     else 
     { 
      //TODO: Log no role, HIGH 
     } 

     return role; 
    } 
} 
+0

http://stackoverflow.com/questions/1452418/how-do-i-mock-the-httpcontext-in-asp-net-mvc-using-moq –

+0

@PaulAbbott嘲笑HttpContext有什麼好處通過UserManager和RoleManager –

+0

您將從代碼中的'HttpContext'獲得用戶管理器和角色管理器。如果你嘲笑'HttpContext'也返回一個模擬用戶和角色管理器,你根本不需要修改你的代碼。如果你直接模擬用戶和角色管理器,你需要改變你的代碼,以某種方式將它們注入到控制器中,而不是從'HttpContext'中獲取它們。 –

回答

1

你應該嘲笑的UserManager的管理者和RoleManager並傳遞他們到AdminController

+0

我在想,但我有困難。有什麼資源可以推薦如何模擬/實例化UserManager和角色管理器? –

+1

最好的辦法是開始使用某種類型的依賴注入(有很多通過nuget提供的完備文檔庫)。將所有外部依賴注入到一個類中,使得單元測試非常非常簡單。 –

+0

聽起來不錯,但我從哪裏開始都不知道。我會做一些關於依賴注入的研究,非常感謝!如果你有這方面的任何好資源,將不勝感激! –

1

如果你按照我的博客,帖子,你應該已經得到的東西像這樣構造的ApplicationUserManager

public class ApplicationUserManager : UserManager<ApplicationUser> 
{ 
    public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) 
    { 
    // configuration-blah-blah 
    } 
} 

和您的控制器應具有用戶管理器對象注入的構造函數:

public class AdminController : Controller 
{ 
    private readonly ApplicationUserManager userManager; 
    public AdminController(ApplicationUserManager userManager) 
    { 
     this.userManager = userManager; 
    } 
} 

現在爲您的測試 - 你需要一個模擬框架。幾年前,我曾經在每個單獨的測試中使用MOQ,現在嘲笑優先權的框架是NSubstitue,因爲更可讀的語法。當前時間我很少使用嘲笑替代品,更喜歡集成測試,甚至可以浸入數據庫中,但這不是這個問題/討論的目標。

所以對於你測試你需要在測試(SUT)創建系統 - AdminController

[Test] 
public void MaxRole_SuperAdmin() 
{ 
    var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>(); 
    var userManager = new ApplicationUserManager(userStoreStub); 
    var sut = new AdminController(userManager); 

    //TODO set up method substitutions on userStoreStub - see NSubstitute documentation 

    var maxRole = sut.GetMaxRole(SuperAdminUserId); 

    Assert.AreEqual(maxRole, "Super Admin"); 
} 

但這是很笨拙的考驗。你正試圖測試太深的東西。我建議你將GetMaxRole從控制器記錄到一個單獨的類 - 服務類,或者這可以是ApplicationUserManager的一部分,或者可以是QueryHandler,如果你願意的話。無論你怎麼稱呼它,它都不應該是控制器的一部分。我認爲這種方法實際上屬於ApplicationUserManager。這樣你的測試層減少了一個,你只需要創建用戶管理器類,而不是控制器。

鑑於GetMaxRole()方法是ApplicationUserManager測試的一部分將變得更小:是

[Test] 
public void MaxRole_SuperAdmin() 
{ 
    var userStoreStub = NSubstitute.Substitute.For<IUserStore<ApplicationUser>>(); 
    var sut = new ApplicationUserManager(userStoreStub); 

    //TODO set up method substitutions on userStoreStub - see NSubstitute documentation 

    var maxRole = sut.GetMaxRole(SuperAdminUserId); 

    Assert.AreEqual(maxRole, "Super Admin"); 
} 

原因從控制器移出的測試方法如下:

  • 隨着時間的推移我發現控制器很容易經常被添加/移除/替換的新的依賴關係改變。如果你有幾個控制器的測試,你將不得不改變每個測試改變的依賴關係被使用。
  • 如果您要使用GetMaxRole方法AdminController之外的其他地方,您有麻煩了。控制器並不意味着共享除提供HTTP(S)端點以外的方法。
  • 您的方法的邏輯看起來像一個域邏輯或業務邏輯。這應該被徹底測試。控制器不容易測試,因爲它們處理HTTP請求和HttpContext這不是很容易測試(儘管可能)。此外,最好避免將控制器與業務邏輯混合在一起 - 幫助您避免意大利麪代碼綜合症。

我可以永遠繼續下去這個話題,但最好閱讀DI book by Mark SeemanUnit Testing book by Roy Osherove

+0

我遇到的問題是調用GetMaxRole的Post動作也調用RoleManager,而且由於RoleManager.Roles不包含任何內容,因此我的RoleManager發出異常。此外,當我嘗試向ApplictionUserManager添加GetMaxRole()並僅僅測試ApplicationManager而不是控制器時,我得到錯誤Store不實現IUserRoleStore