2017-07-14 7 views
0

いくつかのWeb API統合テストのためにIHttpContextAccessorを嘲笑しています。私の目標は、IHttpContextAccessorを模倣し、NameIdentifier要求とRemoteIpAddressを返すことです。.NET COREのテスト - 模擬IHttpContextAccessor with FakeItEasy

テスト

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

コントローラ

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

重要な行はここにある:

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

はここに失敗:

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

現在_httpContextAccessor.HttpContextは常にnullです。私は..私はここに正しい道の上だかはわからない

+0

モックを作成するHttpContextこれはあなたのモックhttpContextAccessorによって返され、それをあなたのIdentityServiceフィクスチャのコンストラクタに渡します – Mardoxx

答えて

1

このようなテストでは、おそらくTestHostタイプを使用した統合テストを作成し、できるだけモックしない方がよいでしょう。はるかに簡単で、現在の方法ではサポートされていないフィルタ(ルートや認証ルールなど)をテストできます。あなたはここにドキュメントで詳細を読むことができます: https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing

私はここにASP.NETコアフィルター上の私のMSDNの記事の一部としてAPIテストを書く方法を示す良い例があります。 https://msdn.microsoft.com/en-us/magazine/mt767699.aspx

0

修正テストプロジェクト

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

私は今コントローラで行うことができるよ:

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

そして、アイデンティティサービス:

ClaimsPrincipal claimsPrincipal = _httpContextAccessor.HttpContext.User; 

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

return userIdInt; 

もっと良いWA yはHttpContextの偽装です。

関連する問題