1

I have ChangePassword method where I have User.Identity.GetUserId() to find UserId.

Problem: It always return null. Don't understand why.

I read in another post that the GetUserById use below line of code to find Id. I am not sure how do I mock ClaimsTypes.NameIdentifier.

return ci.FindFirstValue(ClaimTypes.NameIdentifier);

ChangePassword method (method to be unit testes)

public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword)
{

    _tstService = new TestService();
    IdentityResult result = await _tstService.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);

    if (!result.Succeeded)
    {
        return GetErrorResult(result);
    }

    return Ok();
}

Unit Test

var mock = new Mock<MyController>();
mock.CallBase = true;
var obj = mock.Object;

obj.ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() };
obj.Request.SetOwinContext(CommonCodeHelper.mockOwinContext());

IPrincipal user = GetPrincipal();
obj.ControllerContext.RequestContext.Principal = user;
var result = await obj.ChangePassword(dto);

//GetPrincipal

public static IPrincipal GetPrincipal()
{
    var user = new Mock<IPrincipal>();
    var identity = new Mock<IIdentity>();
    identity.Setup(x => x.Name).Returns("User1@Test.com");
    identity.Setup(p => p.IsAuthenticated).Returns(true);

    user.Setup(x => x.Identity).Returns(identity.Object);
    Thread.CurrentPrincipal = user.Object;
    return user.Object;
}

IOwinContext mocking code

public static IOwinContext mockOwinContext()
{
    var owinMock = new Mock<IOwinContext>();
    owinMock.Setup(o => o.Authentication.User).Returns(new ClaimsPrincipal());
    owinMock.Setup(o => o.Request).Returns(new Mock<OwinRequest>().Object);
    owinMock.Setup(o => o.Response).Returns(new Mock<OwinResponse>().Object);
    owinMock.Setup(o => o.Environment).Returns(new Dictionary<string, object> { { "key1", 123 } });
    var traceMock = new Mock<TextWriter>();
    owinMock.Setup(o => o.TraceOutput).Returns(traceMock.Object);

    var userStoreMock = new Mock<IUserStore<IfsUser>>();
    userStoreMock.Setup(s => s.FindByIdAsync("User1@ifstoolsuite.com")).ReturnsAsync(new IfsUser
    {
        Id = "User1@test.com",
        FirstName = "Test",
        LastName = "User1",
        Email = "User1@test.com",
        UserName = "User1@test.com",
    });
    var applicationUserManager = new IfsUserManager(userStoreMock.Object);
    owinMock.Setup(o => o.Get<IfsUserManager>(It.IsAny<string>())).Returns(applicationUserManager);
    return owinMock.Object;
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
TheKingPinMirza
  • 7,924
  • 6
  • 51
  • 81
  • This is how to do it with fakeiteasy for those interested: http://stackoverflow.com/a/39898952/2672291 – Haohmaru Oct 06 '16 at 15:13

1 Answers1

6

Your GetPrincipal can be updated to use claims.

public static IPrincipal GetPrincipal() {
    //use an actual identity fake
    var username = "User1@Test.com";
    var identity = new GenericIdentity(username, "");
    //create claim and add it to indentity
    var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
    identity.AddClaim(nameIdentifierClaim);

    var user = new Mock<IPrincipal>();
    user.Setup(x => x.Identity).Returns(identity);
    Thread.CurrentPrincipal = user.Object;
    return user.Object;
}

Here is an example that shows how the above approach works.

public partial class MiscUnitTests {
    [TestClass]
    public class IdentityTests : MiscUnitTests {
        Mock<IPrincipal> mockPrincipal;
        string username = "test@test.com";

        [TestInitialize]
        public override void Init() {
            //Arrange                
            var identity = new GenericIdentity(username, "");
            var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
            identity.AddClaim(nameIdentifierClaim);

            mockPrincipal = new Mock<IPrincipal>();
            mockPrincipal.Setup(x => x.Identity).Returns(identity);
            mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
        }

        [TestMethod]
        public void Should_GetUserId_From_Identity() {

            var principal = mockPrincipal.Object;

            //Act
            var result = principal.Identity.GetUserId();

            //Asserts
            Assert.AreEqual(username, result);
        }

        [TestMethod]
        public void Identity_Should_Be_Authenticated() {

            var principal = mockPrincipal.Object;

            //Asserts
            Assert.IsTrue(principal.Identity.IsAuthenticated);
        }
    }
}

You have some design issues. Creating a concrete TestService will cause problems if it as connecting to an actual implementation. That becomes an integration test. Abstract that dependency as well.

public interface ITestService {
    Task<IdentityResult> ChangePassword(string userId, string oldPassword, string newPassword);
}

public abstract class MyController : ApiController {
    private ITestService service;

    protected MyController(ITestService service) {
        this.service = service;
    }

    public async Task<IHttpActionResult> ChangePassword(string NewPassword, string OldPassword) {

        IdentityResult result = await service.ChangePassword(User.Identity.GetUserId(), OldPassword, NewPassword);

        if (!result.Succeeded) {
            return GetErrorResult(result);
        }

        return Ok();
    }

}

Also you should not mock the System under test. You should mock the dependencies of the SUT. Based on your method to be tested and what you indicated in the comments that MyController is an abstract class, the following test should apply

[TestClass]
public class MyControllerTests {
    public class FakeController : MyController { 
        public FakeController(ITestService service) : base(service) { }
    }

    [TestMethod]
    public void TestMyController() {
        //Arrange
        var mockService = new Mock<ITestService>();
        mockService
            .Setup(m => m.ChangePassword(....))
            .ReturnsAsync(....);
        var controller = new FakeController(mockService.Object);

        //Set a fake request. If your controller creates responses you will need this
        controller.Request = new HttpRequestMessage {
            RequestUri = new Uri("http://localhost/api/my")
        };
        controller.Configuration = new HttpConfiguration();
        controller.User = GetPrincipal();

        //Act
        var result = await controller.ChangePassword("NewPassword", "OldPassword");

        //Assert
        //...
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • thanks for updating 'GetPrinciple' method to add claims. It works as i was trying to achieve. However, i dint understand your last THIS comment "Also you don't mock the System under test. Based on your method to be tested the following test should apply"... What you mean here? You meant "I am not mocking" OR "I should not mock"? Remember the method to be tested is inside abstract my class. Please be more clear in your point. thanks – TheKingPinMirza Aug 17 '16 at 12:40
  • You should not mock the system under test – Nkosi Aug 17 '16 at 12:41
  • But where I am mocking System Under Test? Which line of code? – TheKingPinMirza Aug 17 '16 at 12:42
  • `var mock = new Mock();` The assumption here is that the controller is the system under test – Nkosi Aug 17 '16 at 12:42
  • Oh Okay, The reason i am doing this is because 'MyController' basically is an 'abstract' class. So i am mocking it to access/directly call its methods. Do you understand and agree with my reason? Feel free to suggest any other better way. – TheKingPinMirza Aug 17 '16 at 12:44
  • Create a `TestController : MyController` and use that as your SUT. – Nkosi Aug 17 '16 at 12:47
  • Thanks for the suggestion. I didn't opt this way because its an overhead to project. Why to add/create dummy class file in solution? just to support unit test for abstract class. Do you see any other reason? – TheKingPinMirza Aug 17 '16 at 12:49
  • How is it an overhead? It is only for testing not for production code. You cannot create an instance of abstract class. If you want to test it then you need a concrete implementation. – Nkosi Aug 17 '16 at 12:52
  • I see your point here and thanks for your suggestion and solving my issue. Because abstract class itself has some concrete implementation of methods. I am unit testing them and followed this pattern. – TheKingPinMirza Aug 17 '16 at 12:58
  • Then you have some design issues. creating a concrete `TestService` will cause problems if it as connecting to an actual implementation. that becomes an integration test. abstract that dependency as well. – Nkosi Aug 17 '16 at 13:02
  • in your answer, mockService .Setup(m => m.ChangePassword(....)) .ReturnsAsync(....); these dots should be replaced with what? – TheKingPinMirza Aug 20 '16 at 13:53
  • based on 'ITestService' it takes 3 string parameters. You could use Moq's `It.IsAny()` for the parameters. As for the return you can create a dummy `IdentityResult` to satisfy the test. – Nkosi Aug 20 '16 at 14:53
  • i tried like this " IdentityResult iresult = new IdentityResult(); var mockService = new Mock() .Setup(o => o.ChangePassword(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(iresult);" but it gives error Invalid setup on a non-virtual (overridable in VB) member: o => o.ChangePassword(It.IsAny(), It.IsAny(), It.IsAny()) – TheKingPinMirza Aug 20 '16 at 18:14
  • You should mock the interface not the class – Nkosi Aug 20 '16 at 18:21
  • I think i am completely lost. let me gather myself and understand your point. – TheKingPinMirza Aug 20 '16 at 18:24
  • reread my answer and let me know what you are not sure about. – Nkosi Aug 20 '16 at 18:26