Java 如何为具有重试逻辑的方法编写junit测试用例

Java 如何为具有重试逻辑的方法编写junit测试用例,java,junit,retry-logic,Java,Junit,Retry Logic,重试方法精确地查找特定文件的文件夹,并返回该文件(如果存在)。它的最大重试次数为3次,并在两次重试之间休眠1分钟。如果在最大重试次数后文件不存在,则引发一些异常 方法是这样的 File getFile(int retryCount){ File file; file =getFilefromLocation(); if(file!=null) return file if(file==null and retryCount>0) Thread.sle

重试方法精确地查找特定文件的文件夹,并返回该文件(如果存在)。它的最大重试次数为3次,并在两次重试之间休眠1分钟。如果在最大重试次数后文件不存在,则引发一些异常

方法是这样的

File getFile(int retryCount){
    File file;
    file =getFilefromLocation();
    if(file!=null) return file
    if(file==null and retryCount>0)
    Thread.sleep(1)
     --retryCount;
    File filePicked =getFile( retryCount)
   }
else return null;
}

一种可能的方法是将检查文件是否存在的部分分解到它自己的对象中,并模拟该对象。

我将配置重试时间,并将其设置为1秒以进行测试。 您可以在文件已经存在、从未存在的位置进行测试,也可以在将其添加为后台线程的位置进行测试。大约需要5秒钟


<>我也会考虑更短的重试次数,例如每1秒120次,而不是每分钟3次。

< P>我会尝试给出一些指针。 1) 在这里,我不会模拟整个逻辑来检查文件的存在,因为它是被测试方法行为的一部分。
您想要模拟的是实际尝试检索文件的部分,即调用
getFilefromLocation()

2) 在两次重试之间使用一个短的延迟是一个好主意,但这足以使您的测试变得健壮吗?
这不是真的,因为您还必须声明调用了
Thread.sleep()
,并且具有预期的值。在单元测试中不检查这一点意味着任何人都可以删除
getFile()
方法中的
Thread.sleep()
,测试仍然会通过。你真的不想那样。
请注意,您不能轻松地模拟它:它是
静态的
。您应该将此语句移动到可以模拟的
DelayService

3) 实际上,您可以参数化
retryCount
。在编写单元测试时,您希望根据您的需求验证组件的行为。因此,您还应该确保依赖于
getFile(int-retryCount)
的类有效地传递预期参数:
3

此外,如果不希望允许超过某个重试次数,还必须在方法中添加此检查,并在单元测试中断言此规则

根据前两点,您的实际代码可以重构为:

private FileLocator fileLocator; // added dependency
private DelayService delayService; // added dependency

//  constructor to set these dependencies
public MyService(FileLocator fileLocator, DelayService delayService){
    ...
}

File getFile(int retryCount){
    File file;
    file = fileLocator.getFilefromLocation(); // -> change here
    if(file!=null) return file
    if(file==null and retryCount>0){
       delayService.waitFor(); // -> other change here
       --retryCount;
       File filePicked = getFile(retryCount)
    }
    else return null;
}
关于单元测试部分,您可以使用Mockito进行模拟,并将“简单”单元测试与参数化测试混合在一起,因为某些场景具有与实际重试次数类似的行为。
例如,重试0、1、2和3次并查找文件有4种情况可以参数化。
相反,找不到文件不需要参数化,因为所有重试都应该进行,以验证行为

下面是一个依赖JUnit5和Mockito(未测试)的代码示例,您可以根据实际代码进行调整。我只是用参数化测试来说明。其他测试用例的实现不应该更复杂

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.api.Assertions;
import org.mockito.Mockito;
import org.mockito.Mock;

private static final long EXPECTED_DELAY_MN = 1; 
private static final long RETRY_COUNT = 3; 

@Mock
FileLocator fileLocatorMock;

@Mock
private DelayService delayServiceMock;


MyServiceTest myServiceTest;

public MyServiceTest(){
   myServiceTest = new MyServiceTest(fileLocatorMock, delayServiceMock);
}

@ParameterizedTest
@ValueSource(ints = { 0, 1, 2, 3 })
public void getFileThatFailsMultipleTimeAndSuccess(int nbRetryRequired){    

    // failing find
    for (int i=0; i < nbRetryRequired; i++) {
        Mockito.when(fileLocatorMock.getFilefromLocation(...)).thenReturn(null);
    }

    // successful find
    File fileByMock = new File(...); //fake file
    Mockito.when(fileLocatorMock.getFilefromLocation(...)).thenReturn(fileByock);
    File actualFile = myServiceTest.getFile(RETRY_COUNT);

    // assertions
    Mockito.verify(delayServiceMock, Mockito.times(nbRetryRequired)) 
           .waitFor();
    Assert.assertSame(fileByMock, actualFile);

   }
}
import org.junit.jupiter.params.ParameterizedTest;
导入org.junit.jupiter.params.provider.ValueSource;
导入org.junit.jupiter.api.Assertions;
导入org.mockito.mockito;
导入org.mockito.Mock;
私有静态最终长期预期延迟=1;
私有静态最终长时间重试\u计数=3;
@嘲弄
filelocatorfilelocatormock;
@嘲弄
私人延迟服务延迟服务模拟;
MyServiceTest MyServiceTest;
公共MyServiceTest(){
myServiceTest=新的myServiceTest(fileLocatorMock、delayServiceMock);
}
@参数化测试
@ValueSource(整数={0,1,2,3})
public void GetFileThatFailsMultipleTime和Success(int-nbRetryRequired){
//失败的发现
对于(int i=0;i
我只会模拟文件并使文件可用,比如1分钟或2分钟后,断言文件存在或断言不可用,但我无法控制实际方法的重试次数。我想知道的是如何知道该方法重试了多少次。您将看到模拟中的重试次数对象。可能性可能因实际代码而异。你能发布你正在测试的代码吗?你可以从设置延迟时间开始。所以你可以在测试中睡1秒而不是1分钟。