2016-06-29 20 views
0

Ich würde gerne wissen, ob jemand mir dabei helfen kann.Mock ApplicationUserManager für Unit Testing MVC-Controller

Ich schreibe Komponententests für einen bestimmten Controller. Das Controller erbt von einem Base und Base hat diese Eigenschaft:

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

Der Ctor für ApplicationUserManager ist:

public ApplicationUserManager(IUserStore<ApplicationUser> store, IIdentityMessageService emailService) 
     : base(store) 
    { 
     this.EmailService = emailService; 

     var dataProtectionProvider = Startup.DataProtectionProvider; 
     this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity")); 
    } 

Dies ist, was ich die ApplicatonUserManager Klasse zu verspotten tue:

var store = new Mock<IUserStore<ApplicationUser>>(); 
var emailService = new Mock<IIdentityMessageService>(); 
var applicationUserManager = new Mock<ApplicationUserManager>(store.Object, emailService.Object); 
this.targetController.UserManager = applicationUserManager.Object; 
var dataprotectionprovided = new Mock<IDataProtectionProvider>(); 
applicationUserManager.Setup(r => r.UserTokenProvider).Returns(new DataProtectorTokenProvider<ApplicationUser, string>(dataprotectionprovided.Object.Create("ASP.NET Identity"))); 
this.targetController.UserManager = applicationUserManager.Object; 

Ich habe versucht, dies zu verspotten, aber weil dies keine virtuelle Eigenschaft (UserTokenProvider) ist, erlaubt es mir nicht, und ich bekomme diese Ausnahme:

System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: r => r.UserTokenProvider 

Kann mir jemand bei diesem Problem helfen? Ich will nur das, um verspotten den Controller zu testen, die von einem Basecontroller erbt, der diese Eigenschaft hat ..

Dank

+1

Anstatt spöttisch die ApplicationUserManager mißbrauchen, können Sie einen anderen Konstruktor für sie erstellen und über den Ersatz Konstruktor instanziiert? – bwyn

Antwort

1

Danke für Ihre Hilfe @bwyn

Ich habe es geschafft, es zu knacken mit Ihrem Vorschlag. Gerade erstellt einen neuen Konstruktor für die ApplicationUserManager wie folgt aus:

public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) 
    { 
    } 

Und dann die Unit Testing:

var user = new ApplicationUser { User = new User { UserId = 1 } }; 

     var store = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict); 
     store.As<IUserStore<ApplicationUser>>().Setup(x => x.FindByIdAsync(It.IsAny<string>())).ReturnsAsync(user); 
     this.targetController.UserManager = new ApplicationUserManager(store.Object); 

Vielen Dank!

+0

Ich bin froh, dass Sie eine Lösung gefunden haben, aber wenn ein anderer Entwickler versucht, ApplicationUserManager zu verwenden, wie verhindern Sie, dass sie eine Instanz mit Ihrem Testkonstruktor erstellen (von der ich annehme, dass sie in einer realen Implementierung nicht verwendet wird)? –

+0

Hallo @ mark_h das ist ein guter Punkt. Aber wenn ich diesen Controller teste, um die Funktionalität und Logik zu testen, nehme ich immer an, dass der Benutzer immer angemeldet ist und wir eine gültige Benutzerkennung aus der Klasse ApplicationUserManager erhalten.In der realen Welt, weil ich 2 Konstruktoren habe, werde ich die richtige verwenden, wo wir IUserStore und IIdentityMessageService injizieren. Hat das deine Frage beantwortet? Danke – legollas007

0

Wie in einem der Kommentare in Ihrer Antwort erwähnt, kann das Erstellen eines zusätzlichen Konstruktors dazu führen, dass ein anderer Entwickler den falschen verwendet.

Kann ich vorschlagen, dass Sie stattdessen Folgendes versuchen.

Verschieben Sie den Code innerhalb Ihres ursprünglichen ApplicationUserManager-Konstruktors in eine neue geschützte virtuelle Methode. Passen Sie dann den nun leeren Konstruktor an, um eine neue virtuelle Methode aufzurufen.

public class ApplicationUserManager 
{ 
    public ApplicationUserManager(IUserStore<ApplicationUser> store, IIdentityMessageService emailService) 
: base(store) 
    { 
     CalledAfterConstruction(); 
    } 

    protected virtual void CalledAfterConstruction() 
    { 
     this.EmailService = emailService; 

     var dataProtectionProvider = Startup.DataProtectionProvider; 
     this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity")); 
    } 
} 

nun eine neue MockApplicationUserManager Klasse erstellen, wenn Unit-Tests, die ApplicationUserManager erbt, überschreiben die geerbte ‚CalledAfterConstruction‘ mit nur einem leeren Methode und Sie haben ziemlich viel Dinge so, wie Sie in Ihrer Antwort haben.

public class MockClass : ApplicationUserManager 
{ 
    public MockClass(IUserStore<ApplicationUser> store, IIdentityMessageService emailService) : base(store, emailService) 
    { 
    } 

    protected override void CalledAfterContruction() 
    { 

    } 
} 

Ja, das ist ein wenig komplizierter, aber es Menschen aus stoppt Ihre ursprüngliche Klasse

+1

Der Aufruf von virtuellen Methoden im Konstruktor ist eine Fehlerquelle http://www.matthewedmondson.info/2012/08/virtual-method-call-in-contructor.html –