【问题标题】:how to write a junit test case for a method that has retry logic如何为具有重试逻辑的方法编写 junit 测试用例
【发布时间】:2018-07-28 16:32:49
【问题描述】:

重试方法精确地查看特定文件的文件夹并返回该文件(如果存在)。它的最大重试计数为 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 秒而不是 1 分钟。

标签: java junit retry-logic


【解决方案1】:

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

【讨论】:

  • 我只会模拟文件并在 1 分钟或 2 分钟后使文件可用,并断言它存在或断言不可用,但我无法控制实际方法重试了多少次。我想知道的是如何知道方法重试了多少次。
  • 您会看到模拟对象中的重试次数。
【解决方案2】:

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

我也会更频繁地考虑更短的重试时间,例如每 1 秒尝试 120 次,而不是每分钟 3 次。

【讨论】:

    【解决方案3】:

    我会尝试给出一些指示。

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

    2) 在重试之间使用短暂的延迟是一个想法,但这足以使您的测试稳健吗?
    不是真的,因为您还必须断言 Thread.sleep() 已被调用并具有预期值。在单元测试中不检查意味着任何人都可以在 getFile() 方法中删除 Thread.sleep() 并且测试仍然会通过。你真的不想那样。
    请注意,您不能轻易模拟它:它是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);
    
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-02-17
      • 2022-01-05
      • 1970-01-01
      • 1970-01-01
      • 2016-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多