Interfaces permettant de se moquer de ConfirmEmailAsync et d’autres méthodes UserManager dans MVC5

J’essaie de tester à l’unité cette méthode de contrôleur, qui sort des sentiers battus dans les projets MVC actuels.

[AllowAnonymous] public async Task ConfirmEmail(ssortingng userId, ssortingng code) { if (userId == null || code == null) { return View("Error"); } var result = await UserManager.ConfirmEmailAsync(userId, code); return View(result.Succeeded ? "ConfirmEmail" : "Error"); } 

AccountController a un constructeur qui prend un ApplicationUserManager et un ApplicationSignInManager en tant que parameters, ainsi que les propriétés correspondantes avec les régulateurs privés à utiliser pour les tests. Cependant, je ne vois pas comment simuler la méthode ConfirmEmailAsync.

Vous pouvez simuler diverses interfaces dans l’espace de noms Identity:

 var store = new Mock<IUserStore>(); store.As<IUserEmailStore>() .Setup(x => x.FindByIdAsync("username1")) .ReturnsAsync((ApplicationUser)null); var mockManager = new ApplicationUserManager(store.Object); AccountController ac = new AccountController(mockManager, null, GetMockRepository().Object, GetMockLogger().Object); 

Mais je ne peux ni trouver ni comprendre l’interface dont j’ai besoin pour créer une maquette de ConfirmEmailAsync.

Comment puis-je m’y prendre? Et pour référence, existe-t-il un bon moyen de savoir sur quelles interfaces ces méthodes sont activées afin de les simuler et de les tester?

ConfirmEmailAsync ne fait actuellement pas partie d’une interface dans la structure. Il se trouve dans la UserManager , qui est la classe de base du cadre Identity.

Ma solution?

Abstrait toutes les choses

J’ai contourné ce problème en intégrant la plupart des fonctionnalités d’identité dans son propre projet afin de pouvoir le tester à l’unité et de réutiliser l’abstraction dans d’autres projets. J’ai eu l’idée après avoir lu cet article

Identité ASP.NET avec des modèles ignorée par la persistance

J’ai ensuite ajusté l’idée en fonction de mes besoins. En gros, j’ai simplement échangé tout ce dont j’avais besoin d’asp.net.identity pour mes interfaces personnalisées qui reflétaient plus ou moins les fonctionnalités fournies par le cadre, mais avec l’avantage d’une plus grande mocabilité.

IIdentityUser

 ///  /// Minimal interface for a user with an id of type  ///  public interface IIdentityUser : IIdentityUser { } ///  /// Minimal interface for a user ///  public interface IIdentityUser where TKey : System.IEquatable { TKey Id { get; set; } ssortingng UserName { get; set; } ssortingng Email { get; set; } bool EmailConfirmed { get; set; } ssortingng EmailConfirmationToken { get; set; } ssortingng ResetPasswordToken { get; set; } ssortingng PasswordHash { get; set; } } 

IIdentityManager

 ///  /// Exposes user related api which will automatically save changes to the UserStore ///  public interface IIdentityManager : IIdentityManager { } ///  /// Exposes user related api which will automatically save changes to the UserStore ///  public interface IIdentityManager : IIdentityManager where TUser : class, IIdentityUser { } ///  /// Exposes user related api which will automatically save changes to the UserStore ///  public interface IIdentityManager : IDisposable where TUser : class, IIdentityUser where TKey : System.IEquatable { Task AddPasswordAsync(TKey userid, ssortingng password); Task ChangePasswordAsync(TKey userid, ssortingng currentPassword, ssortingng newPassword); Task ConfirmEmailAsync(TKey userId, ssortingng token); //...other code removed for brevity } 

IIdentityResult

 ///  /// Represents the minimal result of an identity operation ///  public interface IIdentityResult : System.Collections.Generic.IEnumerable { bool Succeeded { get; } } 

Dans mon implémentation par défaut du gestionnaire d’identités, j’ai simplement encapsulé ApplicationManager , puis mappé les résultats et les fonctionnalités entre mes types et les types asp.net.identity.

 public class DefaultUserManager : IIdentityManager { private ApplicationUserManager innerManager; public DefaultUserManager() { this.innerManager = ApplicationUserManager.Instance; } //..other code removed for brevity public async Task ConfirmEmailAsync(ssortingng userId, ssortingng token) { var result = await innerManager.ConfirmEmailAsync(userId, token); return result.AsIIdentityResult(); } //...other code removed for brevity } 

Disclaimer: Je travaille chez Typemock.

En fait, vous n’avez besoin d’aucune interface si vous utilisez Typemock , il vous suffit de simuler IdentityResult dont vous avez besoin et de modifier le comportement de la méthode asynchrone “ConfirmEmailAsync”, par exemple un test qui vérifie le scénario d’un email non confirmé:

 [TestMethod, Isolated] public async Task TestWhenEmailIsBad_ErrorMessageIsShown() { // Arrange // Create the wanted controller for testing and fake IdentityResult var controller = new aspdotNetExample.Controllers.AccountController(); var fakeIdentityRes = Isolate.Fake.Instance(); // Fake HttpContext to return a fake ApplicationSignInManager var fakeSIM = Isolate.WhenCalled(() => controller.UserManager).ReturnRecursiveFake(); // Modifying the behavior of ConfirmEmailAsync to return fakeIdentityRes Isolate.WhenCalled(() => fakeSIM.ConfirmEmailAsync("", "")).WillReturn(Task.FromResult(fakeIdentityRes)); Isolate.WhenCalled(() => fakeIdentityRes.Succeeded).WillReturn(false); // Act var result = await controller.ConfirmEmail("", "") as ViewResult; // Assert Assert.AreEqual("Error", result.ViewName); }