如何对线程进行单元测试,这需要花费一些时间来执行操作 [英] How to Unit-Test Thread which takes time to perform action
问题描述
我有Thread
,它在程序运行并轮询队列时运行,并检查它是否有对象,如果是,则在该对象上调用方法
I have Thread
which runs while the program runs and polls a queue and check whether it has object and if yes then it calls method on the object
这是代码:
while(isRunning){
synchronized (loginQueue) {
if(loginQueue.peek() != null) {
Object[] loginObjectWithConnection = loginQueue.poll();
tryLogin(loginObjectWithConnection);
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这是tryLogin方法
Here is the tryLogin method
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()
,但我知道这样做不是正确的方法,因为它有时会失败并有时会通过.
Now my problem is that I want to test the invocations of these service methods but when I run the unit tests they won't work as it takes time to reach the point of tryLogin and till then JUnit fails. I tries using Thread.sleep()
but I know it is not the right way to do it as it fails sometimes and pass sometimes.
这是单元测试中的内容
@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));
}
推荐答案
该系统无法以当前形式进行测试:良好的测试质量包括:
The system is untestable in the current form: among good test qualities there are:
- 易于其他程序员理解
- 其他程序员很难破解
- 运行速度快
您要测试的逻辑部分是LoginManager.tryLogin
,这在您的代码段中是私有的.如果要公开记录它(测试是一种记录文件:它们说明了系统的行为方式),则必须公开.
The piece of logic you want to test is LoginManager.tryLogin
, which is private in your snippet. If you want to publicly document it (tests are kind of documentation: they state how the system should behave), it has to be public.
我建议将所有逻辑移到新类中的方法上:Authentication.attempt()
(我建议一个不可变的对象和一个不带任何参数的方法-有人说OO设计中参数的最佳数量为零)
I suggest to move all that logic to a method in a new class: Authentication.attempt()
(I suggest an immutable object and a method that does not take any argument - someone says the optimal number of arguments in OO design is zero).
现在测试是可行的,我还认为您应该摆脱LoginManager.start()
中的所有代码:只需使用ExecutorService
并提交身份验证尝试-这样,您将拥有更快的程序和更少的代码来进行测试,因为困难(棘手的部分)是由Java管理的.
Now that testing is feasible, I also think you should get rid of all that code in LoginManager.start()
: simply use an ExecutorService
and submit authentication attempts - this way you'll have a faster program and less code to test, because the hard (and tricky) part is managed by Java.
这篇关于如何对线程进行单元测试,这需要花费一些时间来执行操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!