I'm stuck on mocking the IHttpContextAccessor for some web api integration tests. My goal is to be able to mock the IHttpContextAccessor and return NameIdentifier claim and RemoteIpAddress.
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@Email.Email", "PasswordHash", "PasswordSalt", status);
}
public static IEnumerable TestCases
{
get
{
//InsertUser_Should_Insert
yield return new TestCaseData(new InsertUserModel
{
FirstName = "FirstName",
LastName = "LastName",
StatusId = 1,
Email = "Email2@Email.Email"
},
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 = "Email2@Email.Email"
},
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@Email.Email"
},
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);
}
}
Controller
[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"]));
}
}
The important line here is:
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();
}
}
Fails here:
if (_httpContextAccessor.HttpContext == null || !Authenticated())
{
throw new AuthenticationException("User is not authenticated.");
}
Currently the _httpContextAccessor.HttpContext is always null. I'm not sure if I'm on the right path here..