【问题标题】:How to Unit-Test Thread which takes time to perform action如何对需要时间执行操作的线程进行单元测试
【发布时间】:2015-10-29 23:36:36
【问题描述】:

我有Thread,它在程序运行时运行并轮询队列并检查它是否有对象,如果有,则调用对象上的方法

代码如下:

while(isRunning){
        synchronized (loginQueue) {
            if(loginQueue.peek() != null) {
                Object[] loginObjectWithConnection = loginQueue.poll();
                tryLogin(loginObjectWithConnection);
            }
        }
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
}

这里是 tryLogin 方法

private void tryLogin(Object[] loginObjectWithConnection) {
        LoginPacket packet = (LoginPacket)loginObjectWithConnection[0];
        Connection connection = (Connection)loginObjectWithConnection[1];

        try {
            if(playerDataService.arevalidCredentials(packet.getUserName(), packet.getPassword())) {

                if(!playerDataService.isPlayerBanned(packet.getUserName())){ //Player exists in the system

                    communicationService.sendTCP(connection, packetFactory.makeLoginFailurePacket(StringConstants.PLAYER_BANNED));

                } else{ //Player is not banned

                }
            } else { // Player does not exist
                communicationService.sendTCP(connection, packetFactory.makeLoginFailurePacket(StringConstants.INVALID_USER));
            }
        } catch (SQLException e) {
            communicationService.sendTCP(connection, packetFactory.makeLoginFailurePacket(StringConstants.SERVER_ERROR));
            e.printStackTrace();
        }
}

现在我的问题是我想测试这些服务方法的调用,但是当我运行单元测试时,它们将无法工作,因为到达 tryLogin 点需要时间,直到那时 JUnit 失败。我尝试使用Thread.sleep(),但我知道这不是正确的方法,因为它有时会失败,有时会通过。

这是我的单元测试中的内容

@Test
public void theExpectedMessageShouldBeSentIfUserIsBanned() throws InterruptedException, SQLException {
    //Arrange
    when(moqLoginQueue.peek()).thenReturn(object);
    when(moqLoginQueue.poll()).thenReturn(object);
    LoginFailurePacket packet = new LoginFailurePacket(StringConstants.PLAYER_BANNED);
    when(moqPacketFactory.makeLoginFailurePacket(StringConstants.PLAYER_BANNED)).thenReturn(packet);
    when(moqPlayerDataService.arevalidCredentials(anyString(), anyString())).thenReturn(true);
    when(moqPlayerDataService.isPlayerBanned(anyString())).thenReturn(true);

    //Act
    loginManager.start();
    Thread.sleep(10); //Dirty hack -.-

    //Assert
    verify(moqCommunicationService).sendTCP(any(Connection.class), eq(packet));
}

【问题讨论】:

    标签: java multithreading unit-testing junit mockito


    【解决方案1】:

    系统在当前形式下是不可测试的:在良好的测试质量中,有:

    • 其他程序员很容易理解
    • 其他程序员很难破解
    • 跑得快

    您要测试的逻辑是LoginManager.tryLogin,它在您的 sn-p 中是私有的。如果您想公开记录它(测试是一种记录:它们说明系统应该如何运行),它必须是公开的。

    我建议将所有逻辑移至新类中的方法:Authentication.attempt()(我建议使用不可变对象和不带任何参数的方法 - 有人说 OO 设计中的最佳参数数量为零) .

    既然测试是可行的,我也认为你应该去掉 LoginManager.start() 中的所有代码:只需使用 ExecutorService 并提交身份验证尝试 - 这样你将拥有一个更快的程序和更少的代码来测试,因为困难(和棘手)的部分由 Java 管理。

    【讨论】:

    • 对于服务器上收到的每个登录请求,您希望我在 run 方法中创建一个 Authentication 类的对象并将可运行对象提交给 ExecutorService 而不是轮询队列吗?
    • 没错。更简单更高效
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    相关资源
    最近更新 更多