2011-08-01 133 views
2

拋出錯誤,因爲HttpContext的的我那叫一個控制器的HttpContext,如:測試我的控制器在.NET MVC

[Authorize(Roles = "Administrador")] 
public class ApuradorController : Controller 
{ 
    private readonly Questiona2011Context _context = new Questiona2011Context(); 

    private readonly AuthenticationService _authenticationService = new AuthenticationService(); 
} 

的HttpContext是AuthenticationService類電話:

public class AuthenticationService 
{ 
    private IPrincipal _user = HttpContext.Current.User; 

    ... 
} 

在我的項目中,在我的實例控制器發生錯誤時,我會測試控制器private IPrincipal _user = HttpContext.Current.User;行:未將對象引用設置爲對象的實例。

我需要測試我的控制器?

回答

3

您錯過的主要問題是如何設計ASP.NET MVC項目進行測試的知識。

您應該設計您的控制器以使用依賴注入。也就是說,控制器不應使用AuthenticationService的具體實現,而應使用IAuthenticationService,其具體實現將在運行時提供。現在,當控制器被創建時,AuthenticationService也被創建。但在測試場景中,HttpContext爲null,並且創建AuthenticationService失敗,導致NullReference異常。如果您通過接口設計該接口,那麼出於測試目的,您將向控制器提供假實現AuthenticationService,並且不會拋出異常。

public interface IAuthenticationService 
{ 
    IPrincipal User {get;} 
} 

public class AuthenticationService : IAuthenticationService 
{ 
    private IPrincipal _user = HttpContext.Current.User; 

    ... 
} 

//the controller 
[Authorize(Roles = "Administrador")] 
public class ApuradorController : Controller 
{ 
    private readonly Questiona2011Context _context = new Questiona2011Context(); 

    private readonly IAuthenticationService _authenticationService; 

    public ApuradorController(IAuthenticationService authenticationService) 
    { 
     _authenticationService = authenticationService; 
    } 
} 

在測試場景中,你可以使用假貨IAuthenticationService實施一些嘲諷庫,例如moq。並通過嘲諷爲它提供價值

var mockAuthenticationService = new Mock<IAuthenticationService>(); 
//setup mockAuthenticationService 

var controller = new ApuradorController(mockAuthenticationService.Object); 

這次它不會拋出異常。

如果您不瞭解單元測試項目設計的原理,上面提到的信息是沒有用的。爲了快速入門,請閱讀this鏈接。爲了進一步閱讀,有關asp.net mvc的地址簿,我會推薦那些由史蒂文桑德森。單元測試控制器設計的主要思想是,您應該有能力向控制器,假存儲庫,服務等提供假組件,並且僅保留單元測試的控制器部分。然後用這些假部件測試控制器迭代。單元測試意味着測試交互。如果交互是正確的,那麼這些組件的真正實現將會是正確的。如果他們錯了,測試失敗。

+0

我做你的建議:var authenticationService = new Mock (); var controller = new ApuradorController(authenticationService);但我收到一個異常:參數類型'Moq.Mock '不能分配給參數類型'App.Services.IAuthenticationService' –

+0

我應該使用新的ApuradorController(authenticationService.Object); ? –

+0

是的,你應該這樣做。另外,如果你想使用該模擬ApplicationService的User屬性,你應該設置()它。請閱讀moq documentation wiki中的Setup() - http://code.google.com/p/moq/wiki/QuickStart – archil

0

你將不得不嘲笑HttpContextBase使其工作。 Hanselman的article可能會幫助你。

1

這看起來不太合適,你在服務層添加一個依賴到System.Web命名空間。最好將用戶名傳遞給你的服務層 - 可能在構造函數中,可能最好是在基類服務類的構造函數中使用,所以它可以在你所有的服務方法中訪問。

abstract class BaseService 
{ 
    procteced IPrinciple _userName; 

    public BaseService(IPrinciple userName) 
    { 
     _userName = userName; 
    } 
} 

class AuthenticationService : BaseService 
{ 
    public AuthenticationService(IPrinciple userName) 
     :base(userName) 
    { 

    } 
} 

在控制器:

AuthenticationService _service = new AuthenticationService(HttpContext.Current.User); 

也許這樣的 - 如果你將要訪問之類的角色等等,你可能會發現創建圍繞ASP.net一個小包裝類從您的服務層實現接口來執行諸如訪問角色/配置文件信息之類的成員類。

0

您可以連接一個HttpContext的(真品),並使用它:

// Arrange 
    HttpContext.Current = 
    new HttpContext(
     new HttpRequest("", "http://tempuri.org", ""), 
     new HttpResponse(new StringWriter())); 
    HttpContext.Current.User = new GenericPrincipal(new GenericIdentity("MyUser"), new[]{"Admin"}); 

    // Call your controller action... 

我個人會去加倍努力,並添加另一個抽象層,並建立做某事。如IPrincipalAccessor,查看所有其他答案以獲取更多詳細信息。

1

正如其他人所說,爲了單元測試你的控制器,你需要設計它們,你可以在運行時替換HTTP上下文(請求,響應等等),因爲你不會有真正的HTTP單元測試時的上下文。

您需要注意的另一件事是,當您通過單元測試(比如說ApuradorController.Index())在您的控制器中調用一個動作時,您將不會自動獲得與ASP.NET相同的執行管道MVC爲您提供了一個運行時間,因此一些屬於「正常」執行的事件不會被觸發。例如,如果您在OnActionExecuting中執行了一些操作,那麼當您在單元測試中調用ApuradorController.Index()時,該方法將不會自動觸發。

測試控制器最初可能很難,因爲它會迫使您改變您的編程方式。最終的結果是更好的代碼,但到達那裏可能會有點挑戰。