2016-07-12 85 views
10

Sto provando a eseguire il test dell'unità il codice del controller che ottiene le informazioni dal ClaimsPrincipal.Current. Nel codice regolatore ICome aggiungere attestazioni in una simulazione ClaimsPrincipal

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

e sto cercando di deridere la mia ClaimsPrincipal con i reclami, ma io ancora non hanno alcun valore finto dalla richiesta.

// 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")); 

Il viewresult.Contenuto è vuoto mentre eseguo il test dell'unità. Qualsiasi aiuto se posso aggiungere la richiesta di finto. Grazie.

risposta

15

In primo luogo, si sta perdendo questa linea nel tuo test:

Thread.CurrentPrincipal = cp.Object; 

(e poi pulirlo in tearDown).

In secondo luogo, come @trailmax ha menzionato, gli oggetti principali di derisione non sono pratici. Nel tuo caso, ClaimsPrincipal.FindFirst (in base alla sorgente decompilata) esamina i campi privati ​​della sua istanza, questo è il motivo per cui il mocking non ha aiutato. funzionalità

Io preferisco usare due classi semplici che mi permettono di unit test basata su attestazioni:

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

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

quindi il test si restringe verso il basso per:

[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")); 
    } 

e ora passa, ho appena verificato

+0

Grazie! Penso che stavo rendendo complicato. Quindi, quando dobbiamo effettivamente prendere in giro "ClaimsPrincipal"? Da quando controllo su google, molta gente sta prendendo in giro 'ClaimsPrincipal'. Ad esempio, http://stackoverflow.com/questions/14190066/is-there-any-way-i-can-mock-a-claims-principal-in-my-asp-net-mvc-web-application. – Henry

+0

Prego :) in questa risposta, l'ipotesi che SUT chiamerà solo il metodo HasClaim, è troppo fragile. Se un giorno SUT (forse codice di terze parti) ha bisogno di accedere a membri aggiuntivi del principale, il test si interromperà. Spesso preferisco subordinare manualmente le dipendenze alle sottoclassi e implementarle in un "modo test", mentre mi assicuro che continuino a comportarsi in modo coerente. –

+0

Brillante! Ha funzionato perfettamente per me Grazie! –

13

Non è necessario per deridere ClaimsPrincipal non ha dipendenze esterne e si può creato un-deriso:

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); 

E io non sono sicuro di quello che si sta testando qui. Certamente "John Doe" non farà parte di viewResult.Content perché non è mai stato impostato su questo.