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

时间:2018-07-28 06:28:49

标签: java junit retry-logic

retry方法精确地查找特定文件的文件夹并返回该文件(如果存在)。它的最大重试次数为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;
}

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

我将使重试时间可配置,并将其设置为1秒进行测试。 您可以对文件已经存在的位置进行测试,从不进行测试,然后将文件添加为后台线程。应该花费大约5秒钟。

我还会考虑更短的重试时间,例如每1秒进行120次尝试,而不是每分钟3次。

答案 2 :(得分:0)

我将尝试给出一些指示。

1)在这里,我不会嘲笑检查文件是否存在的整个逻辑,因为它是被测试方法行为的一部分。
您要模拟的是实际尝试检索文件的部分,即调用getFilefromLocation()时。

2)尝试在两次重试之间使用较短的延迟,但这是否足以使您的测试变得强大?
并非完全如此,因为您还必须断言Thread.sleep()已被调用并且具有预期值。不检查单元测试是否意味着任何人都可以使用Thread.sleep()方法删除getFile(),并且测试仍将通过。你真的不想要那个
请注意,您不能轻易对其进行嘲笑:它是static。您应该将此语句移动到可以模拟的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);

   }
}