当需要对象时,Mock返回null

  • 本文关键字:Mock 返回 null 对象 c# moq
  • 更新时间 :
  • 英文 :


我有一个测试,看起来像这样。

namespace Domain.Tests.Unit.Features.ForUser.Auth
{
[TestClass]
public class LoginHandlerTests
{
[TestMethod]
public async Task Should_Succeede_With_Valid_User()
{
// Arrange
var loginCommand = new LoginHandler.LoginCommand
{
Email = "testemail",
Password = "testpassword"
};

var user = new User
{
Email = loginCommand.Email,
UserName = "testname",
};
var userServiceMock = new UserServiceFixture()
.WithSucceededFindByEmailAsync(loginCommand.Email)
.WithSucceededGetRolesAsync(user)
.GetMock();
// Problematic MOCK
var result = new Mock<ISignInServiceResult>()
.SetupProperty(l => l.Succeeded, true);
var signInServiceMock = new Mock<ISignInService>();
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.Returns(Task.FromResult(result.Object));
var jwtGeneratorServiceMock = new JwtGeneratorServiceFixture()
.WithSucceededGeneration(loginCommand.Email, new string[] { "User" })
.GetMock();
// Here the result is what i expect
//var result1 = await signInServiceMock.Object.CheckPasswordSignInAsync(user, loginCommand.Password);
// Act
var sut = new LoginHandler.Handler(
userServiceMock.Object,
signInServiceMock.Object,
jwtGeneratorServiceMock.Object
);
var loginResponse = await sut.Handle(loginCommand, new CancellationToken());
// Assert
loginResponse.Should().NotBeNull();
loginResponse.Success.Should().BeTrue();
loginResponse.Token.Should().NotBeEmpty();
loginResponse.RefreshToken.Should().NotBeEmpty();

唯一的问题是,当在我的处理程序上调用该方法时,signInServiceMock返回null。如果我在测试中直接调用它,我就会得到预期的结果。但当它在我的处理程序上被调用时,它总是返回null。我已经检查了设置方法和所需的参数,一切似乎都是正确的。知道吗?谢谢处理程序是这样的:

public class Handler : IRequestHandler<LoginCommand, LoginResponse>
{
private readonly IUserService _userService;
private readonly ISignInService _signInService;
private readonly IJwtGeneratorService _jwtGeneratorService;
public Handler(IUserService userService, ISignInService signInService, IJwtGeneratorService jwtGeneratorService)
{
_userService = userService;
_signInService = signInService;
_jwtGeneratorService = jwtGeneratorService;
}
public async Task<LoginResponse> Handle(LoginCommand command, CancellationToken _cancellationToken)
{
var user = await _userService.FindByEmailAsync(command.Email);
if (user is null) throw new InvalidLoginCredentialsException();
ISignInServiceResult checkedPassword 
= await _signInService.CheckPasswordSignInAsync(user, command.Password);
if (!checkedPassword.Succeeded) throw new InvalidLoginCredentialsException();
var roles = await _userService.GetRolesAsync(user);
if (roles is null)
throw new UnableToGetRolesException();
ITokenData? token = _jwtGeneratorService.Generate(user.Email, roles);
if (token is null)
throw new LoginTokenGenerationException();
return new LoginResponse
{
Success = true,
Token = token.Token,
RefreshToken = token.RefreshToken
};
}
}

问题是您的模拟设置与Handler内部使用它的方式不正确。

在你的设置中,你有这个:

var user = new User
{
Email = loginCommand.Email,
UserName = "testname",
};
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.Returns(Task.FromResult(result.Object));

这指示mock只有在接收到这个确切的user实例时才返回所需的结果,而在Handler内部,您正在创建另一个User实例并将其传递给mock方法,如下所示:

// new User is being created:
var user = await _userService.FindByEmailAsync(command.Email);
// No longer matches your setup:
await _signInService.CheckPasswordSignInAsync(user, command.Password);

也许你的意思是:

signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(It.IsAny<User>(), loginCommand.Password))

更新:

实际上,真正的问题可能在于缺少UserServicemock的设置。

尽管您正在呼叫.WithSucceededGetRolesAsync(user),但我相信这不会影响最终呼叫FindByEmailAsync(user)

总而言之,您必须确保对FindByEmailAsync(user)的调用确实会在您的设置中返回相同的user实例。

我相信你想要的是:

signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.ReturnsAsync((User user, string password) => { return result.Object; });

而不是具有CCD_ 11的CCD_。除此之外,调用CheckPasswordSignInAsync方法的代码可能会传递与Mock配置不同的参数值,例如密码的Hash。您可以设置一个断点,并检查传入的值是否与您的测试配置相匹配。

通常使用Mocks时,您会将其配置为预期It.IsAny,并使用以下方式断言值:

signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(It.IsAny<User>(), It.IsAny<string>()))
.ReturnsAsync((User user, string password) => { return result.Object; });

如果Returns需要执行任何简单的计算来模拟,或者您可以验证,例如通过传递的用户和密码是否匹配,它将从这里接收传入的值

signInServiceMock.Verify(l => l.CheckPasswordSignInAsync(It.Is<User>(u => u.EMail == "testemail"), It.Is<string>(p => p == "testpassword")); 

这至少会失败,因为有人描述调用了该方法,但预期的参数不匹配。

最新更新