如何使用 IConfiguration 测试带有令牌的 class

How to test a class with token using IConfiguration

我在 .Net 框架中使用 Xunit。这个 class 是 GenerateJWT 有一个参数是 User type (Object) 需要测试,我想模拟 IConfiguration Interface 但不知道它是否正确。目标是输入一个需要匹配字符串 result

的字符串 expect
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;
        }

下面这个class是我的测试,我用Mockclass来Mock IConfiguration接口那returns一个代码

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

测试消息失败,不知如何处理。我需要你们的一些建议

这里的问题可能是GenerateJWT方法使用了DateTime.Now。 这是您无法直接控制的事情,并且会在测试 运行 时随时更改。在您的测试中,您还通过调用 DateTime.Now 创建了一个令牌,但要检查的令牌和生成的令牌之间可能仍然存在细微的毫秒差异。

如果您希望能够测试此方法,您将需要使用包装器 class 来提供当前时间。 这可能看起来像这样:

public interface IDateTimeProvider
{
   DateTime Now { get; }
}

然后您需要更改 GenerateJWT 方法以也从外部接受 DateTimeProvider 实例:

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

这样您就可以完全控制测试中的所有内容:

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