Home > front end >  How to test a class with token using IConfiguration
How to test a class with token using IConfiguration

Time:04-24

I use Xunit in the .Net framework. This class is GenerateJWT that has a parameter is User type (Object) which needs to be tested, I want to mock the IConfiguration Interface but don't know exactly if it is correct or not. The goal is to input a string expect that needs to match to string result

public string GenerateJWT(User userInfo)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, userInfo.Phone),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };

            var token = new JwtSecurityToken(
                issuer: _config["Jwt:Issuer"],
                audience: _config["Jwt:Issuer"],
                claims,
                expires: DateTime.Now.AddMinutes(120),
                signingCredentials: credentials);

            var encodeToken = new JwtSecurityTokenHandler().WriteToken(token);
            return encodeToken;
        }

This class below is my test, I use Mock class to Mock IConfiguration interface that returns a code

 [Fact]
        public async Task GenerateJWT_Return_Token()
        {  
            //Arrange
            var mock_service = new Mock<IUserAdminService>();
            var mock_config = new Mock<IConfiguration>();
            User login = new User() { Id = 2, FullName = "abc", AddressDetail = "A", CityId = "123", DistrictId = "123", WardsId = "123", Password = "03RCzu/LT/48EeliJG9L/ZS/ITwGAYUUoALJSePkG5k=", Gender = "male", Phone = "0123456789", DOB = "5/5/1999", Avatar = "" };

            var x = "03RCzu/LT/48EeliJG9L/ZS/ITwGAYUUoALJSePkG5k=";
            mock_config.Setup(m => m["Jwt:Key"]).Returns(x);
          
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(x));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, login.Phone),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };
            var token= new JwtSecurityToken(
                x,
                x,
                claims,
                expires: DateTime.Now.AddMinutes(120),
                signingCredentials: credentials);
            //Act
            var service = new LoginAdminService(mock_config.Object, mock_service.Object);

            string result = service.GenerateJWT(login);
            var encodeToken = new JwtSecurityTokenHandler().WriteToken(token);
           
            //Assert
            Assert.Equal(encodeToken, result);
        }

The test got message failure, I don't know how to deal with that. I need some advice from you guys enter image description here

CodePudding user response:

The problem here is probably that the GenerateJWT method is using DateTime.Now. This is someting that you have no direct control over and will change anytime the test is run. In your test you're also creating a token by calling DateTime.Now, but there probably still is a sleight milisecond differnce between the token to check and the generated token.

If you want to be able to test this method you will need to use a wrapper class for providing the current time. This could look something like this:

public interface IDateTimeProvider
{
   DateTime Now { get; }
}

You would then need to change the GenerateJWT method to also accept a DateTimeProvider instance from outside:

public string GenerateJWT(User userInfo, IDateTimeProvider dateProvider) 
{
  ...
  var token = new JwtSecurityToken(
      issuer: _config["Jwt:Issuer"],
      audience: _config["Jwt:Issuer"],
      claims,
      expires: dateProvider.Now.AddMinutes(120),
      signingCredentials: credentials);
  ...
}

This way you can completely control everything from your test:

[Fact]
public async Task GenerateJWT_Return_Token()
{
  //Arrange
        var mock_service = new Mock<IUserAdminService>();
        var mock_config = new Mock<IConfiguration>();
        var mockProvider = new Mock<IDateTimeProvider>();
        User login = new User() { Id = 2, FullName = "abc", AddressDetail = "A", CityId = "123", DistrictId = "123", WardsId = "123", Password = "03RCzu/LT/48EeliJG9L/ZS/ITwGAYUUoALJSePkG5k=", Gender = "male", Phone = "0123456789", DOB = "5/5/1999", Avatar = "" };

        var x = "03RCzu/LT/48EeliJG9L/ZS/ITwGAYUUoALJSePkG5k=";
        mock_config.Setup(m => m["Jwt:Key"]).Returns(x);

        mockProvider.Setup(m => m.Now).Returns(new DateTime(2022, 4, 22, 16, 0, 0));
      
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(x));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, login.Phone),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };
        var token= new JwtSecurityToken(
            x,
            x,
            claims,
            expires: mockProvider.Object.Now.AddMinutes(120),
            signingCredentials: credentials);
        //Act
        var service = new LoginAdminService(mock_config.Object, mock_service.Object);

        string result = service.GenerateJWT(login, mockProvider.Object);
        var encodeToken = new JwtSecurityTokenHandler().WriteToken(token);
       
        //Assert
        Assert.Equal(encodeToken, result);
}
  • Related