2016-07-12 14 views
10

我想單元測試從ClaimPrincipal.Current獲取信息的控制器代碼。 在控制器代碼我如何在模擬中添加聲明ClaimsPrincipal

public class HomeController { 
    public ActionResult GetName() { 
     return Content(ClaimsPrincipal.Current.FindFirst("name").Value); 
    } 
} 

,我試圖模仿我ClaimsPrincipal與索賠,但我仍然沒有從索賠的任何模擬值。

// Arrange 
IList<Claim> claimCollection = new List<Claim> 
{ 
    new Claim("name", "John Doe") 
}; 

var identityMock = new Mock<ClaimsIdentity>(); 
identityMock.Setup(x => x.Claims).Returns(claimCollection); 

var cp = new Mock<ClaimsPrincipal>(); 
cp.Setup(m => m.HasClaim(It.IsAny<string>(), It.IsAny<string>())).Returns(true); 
cp.Setup(m => m.Identity).Returns(identityMock.Object); 

var sut = new HomeController(); 

var contextMock = new Mock<HttpContextBase>(); 
contextMock.Setup(ctx => ctx.User).Returns(cp.Object); 

var controllerContextMock = new Mock<ControllerContext>(); 
controllerContextMock.Setup(con => con.HttpContext).Returns(contextMock.Object); 
controllerContextMock.Setup(con => con.HttpContext.User).Returns(cp.Object); 

sut.ControllerContext = controllerContextMock.Object; 

// Act 
var viewresult = sut.GetName() as ContentResult; 

// Assert 
Assert.That(viewresult.Content, Is.EqualTo("John Doe")); 

viewresult.Content爲空,因爲我運行單元測試。任何幫助,如果我可以添加模擬索賠。謝謝。

回答

15

首先,你缺少這條線在您的測試:

Thread.CurrentPrincipal = cp.Object; 

(再清洗它在TearDown中)。

其次,正如@trailmax所提到的,嘲笑主體對象是不切實際的。在你的情況下,ClaimsPrincipal.FindFirst(根據反編譯源)查看其實例的私人領域,這就是嘲笑沒有幫助的原因。基於聲明的

我喜歡用兩個簡單的類,允許我的單元測試功能:

public class TestPrincipal : ClaimsPrincipal 
    { 
     public TestPrincipal(params Claim[] claims) : base(new TestIdentity(claims)) 
     { 
     } 
    } 

    public class TestIdentity : ClaimsIdentity 
    { 
     public TestIdentity(params Claim[] claims) : base(claims) 
     { 
     } 
    } 

那麼你的測試縮小到:

[Test] 
    public void TestGetName() 
    { 
     // Arrange 
     var sut = new HomeController(); 
     Thread.CurrentPrincipal = new TestPrincipal(new Claim("name", "John Doe")); 

     // Act 
     var viewresult = sut.GetName() as ContentResult; 

     // Assert 
     Assert.That(viewresult.Content, Is.EqualTo("John Doe")); 
    } 

,它通過了,我已經剛剛驗證。

+0

謝謝!我認爲我讓它變得複雜。那麼我們什麼時候需要模擬'ClaimsPrincipal'?自從我檢查谷歌時,很多人都在嘲笑'ClaimsPrincipal'。像這樣,http://stackoverflow.com/questions/14190066/is-there-any-way-i-can-mock-a-claims-principal-in-my-asp-net-mvc-web-application。 – Henry

+0

不客氣:)在這個答案中,SUT只會調用HasClaim方法的假設太脆弱了。如果有一天SUT(可能是第三方代碼)需要訪問委託人的其他成員,測試將會中斷。我經常更喜歡手動繼承依賴關係並以「測試方式」實現它們,同時我確保它們仍然保持一致。 –

+0

太棒了!爲我完美工作。謝謝! –

13

你並不需要模擬ClaimsPrincipal它沒有外部依賴性,你可以創建它未嘲笑:

var claims = new List<Claim>() 
{ 
    new Claim(ClaimTypes.Name, "username"), 
    new Claim(ClaimTypes.NameIdentifier, "userId"), 
    new Claim("name", "John Doe"), 
}; 
var identity = new ClaimsIdentity(claims, "TestAuthType"); 
var claimsPrincipal = new ClaimsPrincipal(identity); 

而且我不知道你在這裏測試的內容。當然,「John Doe」不會成爲viewResult.Content的一部分,因爲它從來沒有被設置爲這個。