即使断言不正确,模拟和单元测试也会通过

本文关键字:单元测试 模拟 断言 不正确 | 更新日期: 2023-09-27 17:56:17

我对单元测试和TDD整体上很陌生。

我的服务层 (WCF) 中有以下登录方法,该方法作为 Windows 服务托管。我的服务层遵循外观模式,对服务层的所有调用都是使用请求对象进行的,并返回相应的响应对象。

public LoginResponse Login(LoginRequest request)
{
    var response = new LoginResponse(request.RequestId);
    try
    {
        if (!ValidRequest(request, response, Validate.ClientTag))
            return response;
        var user = userDao.GetByUserId(request.UserId);
        if (user != null)
        {                   
            if (request.Password != "")
            {
                var authenticationService = new AuthenticationService();
                if (authenticationService.Authenticate(user, request.Password))
                {                          
                    return response;
                }                        
                response.ErrorCode = "IncorrectPassword";
            }
            else
            {                       
                response.ErrorCode = "PasswordNotFound";
            }
        }
        else
        {
            response.ErrorCode = "UserNotFound";
        }
        response.Acknowledge = AcknowledgeType.Failure;
        return response;
    }
    catch (Exception ex)
    {
        Log.Error(ex);
        response.Acknowledge = AcknowledgeType.Failure;
        response.ErrorCode = "Exception";
        return response;
    }
}

两行都在这里:

userDao.GetByUserId(request.UserId);

authenticationService.Authenticate(user, request.Password);

正在调用数据库。

这是我用xUnit和JustMock编写的测试:

    [Theory]
    [InlineData("manager", "manager")]
    public void LoginTest(string userId, string password)
    {
        //Arrange
        var userServiceMock = Mock.Create<IUserManagementService>();
        var request = new LoginRequest().Prepare();
        request.UserId = userId;
        request.Password = password;
        var response = new LoginResponse(request.RequestId);
        Mock.Arrange(() => userServiceMock.Login(request)).Returns(response).OccursOnce();
        //Act
        userServiceMock.Login(request);
        //Assert
        Mock.Assert(userServiceMock);            
    }

我遇到的问题是,即使我将服务方法更改为

public LoginResponse Login(LoginRequest request)
{
    return null;
}

我的测试仍然通过。我做错了什么?

即使断言不正确,模拟和单元测试也会通过

你没有测试任何东西。您正在模拟您正在模拟您的测试系统,这是不正确的。

请参阅此答案,了解什么是模拟的简要说明。

如果您正在测试UserManagementService.Login()则需要:

[Theory]
[InlineData("manager", "manager")]
public void LoginTest(string userId, string password)
{
    // Arrange
    // System under test
    IUserManagementService userService = new UserManagementService();
    var request = new LoginRequest().Prepare();
    request.UserId = userId;
    request.Password = password;
    var expectedResponse = new LoginResponse(request.RequestId);
    //Act
    var actualResponse = userService.Login(request);
    //Assert
    Assert.AreEqual(actualResponse.Something, expectedResponse.Something);            
}

无需嘲笑任何东西。(如果你不希望单元测试写入日志,你可能需要模拟Login()具有的任何依赖项,例如AuthenticationServiceuserDao甚至Log

你已经模拟了要测试的类。在 userServiceMock 上调用 Login 时,以下代码行是相关的:

Mock.Arrange(() => userServiceMock.Login(request)).Returns(response).OccursOnce();

也就是说,您始终返回有效的结果。实际代码永远不会执行。

每个单元测试都有一个"待测系统"(SUT)。你永远不会想嘲笑SUT本身。您只模拟 SUT 所依赖的任何其他对象。在您的情况下,UserService似乎是您的 SUT。没有理由嘲笑用户服务,你想测试真正的服务!

然而,SUT似乎使用了一种可能被注入到某处的UserDao。注入用户DAO的模拟而不是真实的模拟。

此外,它还使用AuthenticationService。不幸的是,它没有注入,但您的方法会动态创建一个实例。这使得无法使用它的模拟实现。您将需要重构您的用户服务,以便您可以注入要使用的AuthenticationService。然后你的单元测试可以注入AuthenticationService的模拟。