2017-07-14 4 views
0

Je suis bloqué sur moquer l'IHttpContextAccessor pour certains tests d'intégration de l'API Web. Mon but est de pouvoir se moquer du IHttpContextAccessor et de renvoyer la revendication NameIdentifier et RemoteIpAddress..NET CORE Testing - Mock IHttpContextAccessor avec FakeItEasy

test

public class InsertUser : TestBase 
{ 
    private UserController _userController; 

    [OneTimeSetUp] 
    public void OneTimeSetUp() 
    { 
     IStringLocalizer<UserController> localizer = A.Fake<IStringLocalizer<UserController>>(); 

     _userController = new UserController(localizer, Mapper, UserService, StatusService, IdentityService); 
     _userController.ControllerContext = A.Fake<ControllerContext>(); 
     _userController.ControllerContext.HttpContext = A.Fake<DefaultHttpContext>(); 

     var fakeClaim = A.Fake<Claim>(x => x.WithArgumentsForConstructor(() => new Claim(ClaimTypes.NameIdentifier, "1"))); 
     var fakeIdentity = A.Fake<ClaimsPrincipal>(); 

     A.CallTo(() => fakeIdentity.FindFirst(ClaimTypes.NameIdentifier)).Returns(fakeClaim); 
     A.CallTo(() => _userController.ControllerContext.HttpContext.User).Returns(fakeIdentity); 

     StatusTypeEntity statusType = ObjectMother.InsertStatusType(StatusTypeEnum.StatusType.User); 
     StatusEntity status = ObjectMother.InsertStatus(StatusEnum.Status.Active, statusType); 
     ObjectMother.InsertUser("FirstName", "LastName", "[email protected]", "PasswordHash", "PasswordSalt", status); 
    } 

    public static IEnumerable TestCases 
    { 
     get 
     { 
      //InsertUser_Should_Insert 
      yield return new TestCaseData(new InsertUserModel 
      { 
       FirstName = "FirstName", 
       LastName = "LastName", 
       StatusId = 1, 
       Email = "[email protected]" 
      }, 
       1, 
       2).SetName("InsertUser_Should_Insert"); 

      //InsertUser_Should_Not_Insert_When_StatusId_Not_Exist 
      yield return new TestCaseData(new InsertUserModel 
      { 
       FirstName = "FirstName", 
       LastName = "LastName", 
       StatusId = int.MaxValue, 
       Email = "[email protected]" 
      }, 
       1, 
       1).SetName("InsertUser_Should_Not_Insert_When_StatusId_Not_Exist"); 

      //InsertUser_Should_Not_Insert_When_Email_Already_Exist 
      yield return new TestCaseData(new InsertUserModel 
      { 
       FirstName = "FirstName", 
       LastName = "LastName", 
       StatusId = 1, 
       Email = "[email protected]" 
      }, 
       1, 
       1).SetName("InsertUser_Should_Not_Insert_When_Email_Already_Exist"); 
     } 
    } 

    [Test, TestCaseSource(nameof(TestCases))] 
    public async Task Test(InsertUserModel model, int userCountBefore, int userCountAfter) 
    { 
     //Before 
     int resultBefore = Database.User.Count(); 

     resultBefore.ShouldBe(userCountBefore); 

     //Delete 
     await _userController.InsertUser(model); 

     //After 
     int resultAfter = Database.User.Count(); 

     resultAfter.ShouldBe(userCountAfter); 
    } 
} 

Contrôleur

[Route("api/administration/[controller]")] 
[Authorize(Roles = "Administrator")] 
public class UserController : Controller 
{ 
    private readonly IStringLocalizer<UserController> _localizer; 
    private readonly IMapper _mapper; 
    private readonly IUserService _userService; 
    private readonly IStatusService _statusService; 
    private readonly IIdentityService _identityService; 

    public UserController(IStringLocalizer<UserController> localizer, 
     IMapper mapper, 
     IUserService userService, 
     IStatusService statusService, 
     IIdentityService identityService) 
    { 
     _localizer = localizer; 
     _mapper = mapper; 
     _userService = userService; 
     _statusService = statusService; 
     _identityService = identityService; 
    } 

    [HttpPost("InsertUser")] 
    public async Task<IActionResult> InsertUser([FromBody] InsertUserModel model) 
    { 
     if (model == null || !ModelState.IsValid) 
     { 
      return Ok(new GenericResultModel(_localizer["An_unexpected_error_has_occurred_Please_try_again"])); 
     } 

     StatusModel status = await _statusService.GetStatus(model.StatusId, StatusTypeEnum.StatusType.User); 

     if (status == null) 
     { 
      return Ok(new GenericResultModel(_localizer["Could_not_find_status"])); 
     } 

     UserModel userExist = await _userService.GetUser(model.Email); 

     if (userExist != null) 
     { 
      return Ok(new GenericResultModel(_localizer["Email_address_is_already_in_use"])); 
     } 

     UserModel user = _mapper.Map<InsertUserModel, UserModel>(model); 

     var letrTryAndGetUserIdFromNameIdentifier = _identityService.GetUserId(); 

     user.DefaultIpAddress = _identityService.GetIpAddress(); 

     //UserModel insertedUser = await _userService.InsertUser(user, model.Password); 
     UserModel insertedUser = await _userService.InsertUser(user, "TODO"); 

     if (insertedUser != null) 
     { 
      return Ok(new GenericResultModel { Id = insertedUser.Id }); 
     } 

     return Ok(new GenericResultModel(_localizer["Could_not_create_user"])); 
    } 
} 

La ligne importante ici est:

var letrTryAndGetUserIdFromNameIdentifier = _identityService.GetUserId(); 

IdentityService

public class IdentityService : IIdentityService 
{ 
    private readonly IHttpContextAccessor _httpContextAccessor; 

    public IdentityService(IHttpContextAccessor httpContextAccessor) 
    { 
     _httpContextAccessor = httpContextAccessor; 
    } 

    public int GetUserId() 
    { 
     if (_httpContextAccessor.HttpContext == null || !Authenticated()) 
     { 
      throw new AuthenticationException("User is not authenticated."); 
     } 

     ClaimsPrincipal claimsPrincipal = _httpContextAccessor.HttpContext.User; 

     string userIdString = claimsPrincipal.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; 
     int.TryParse(userIdString, out int userIdInt); 

     return userIdInt; 
    } 

    public string GetIpAddress()l 
    { 
     return _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress.ToString(); 
    } 
} 

ne réussit pas ici:

if (_httpContextAccessor.HttpContext == null || !Authenticated()) 
{ 
    throw new AuthenticationException("User is not authenticated."); 
} 

Actuellement, le _httpContextAccessor.HttpContext est toujours nulle. Je ne sais pas si je suis sur la bonne voie ici ..

+0

Créer une maquette HttpContext qui est retourné par votre faux httpContextAccessor que vous transmettez ensuite à votre constructeur de votre appareil IdentityService – Mardoxx

Répondre

1

Pour ce genre de test, il vaudrait probablement mieux écrire un test d'intégration qui utilise le type TestHost, et se moquer le moins possible. Ce sera beaucoup plus simple, et vous pourrez tester les filtres (comme les routes et les règles d'autorisation), ce que votre approche actuelle ne supporte pas. Vous pouvez en savoir plus dans les docs ici: https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing

J'ai un bon échantillon montrant comment écrire des tests API dans le cadre de mon article MSDN sur Filtres ASP.NET de base, ici: https://msdn.microsoft.com/en-us/magazine/mt767699.aspx

0

projet Modified Test

var userIdClaim = A.Fake<Claim>(x => x.WithArgumentsForConstructor(() => new Claim(ClaimTypes.NameIdentifier, "1"))); 

var httpContextAccessor = A.Fake<HttpContextAccessor>(); 
httpContextAccessor.HttpContext = A.Fake<HttpContext>(); 
httpContextAccessor.HttpContext.User = A.Fake<ClaimsPrincipal>(); 
IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); 
A.CallTo(() => httpContextAccessor.HttpContext.Connection.RemoteIpAddress).Returns(ipAddress); 
A.CallTo(() => httpContextAccessor.HttpContext.User.Identity.IsAuthenticated).Returns(true); 
A.CallTo(() => httpContextAccessor.HttpContext.User.Claims).Returns(new List<Claim> { userIdClaim }); 
var identityService = new IdentityService(httpContextAccessor); 
_userController = new UserController(localizer, Mapper, UserService, StatusService, identityService); 

Je suis maintenant en mesure de le faire dans le contrôleur:

var claims = HttpContext.User.Claims.ToList(); 

Et service d'identité:

ClaimsPrincipal claimsPrincipal = _httpContextAccessor.HttpContext.User; 

string userIdString = claimsPrincipal.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; 
int.TryParse(userIdString, out int userIdInt); 

return userIdInt; 

S'il vous plaît laissez-moi maintenant si vous pensez qu'il y a un meilleur wa y pour truquer HttpContext.