Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/385.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 单元测试具有重试逻辑的方法_Java_Testng_Jmockit - Fatal编程技术网

Java 单元测试具有重试逻辑的方法

Java 单元测试具有重试逻辑的方法,java,testng,jmockit,Java,Testng,Jmockit,我有一个带有重试逻辑的方法。重试逻辑是使用自定义注释实现的。我需要一个单元测试来验证在抛出某个异常时是否再次调用该方法 public class FileTest { String first; String second; Instant third; int fourth; @Tested StoreFiles storeFiles; @Injectable FileSystemFactory fileSystemFact

我有一个带有重试逻辑的方法。重试逻辑是使用自定义注释实现的。我需要一个单元测试来验证在抛出某个异常时是否再次调用该方法

public class FileTest {

    String first;
    String second;
    Instant third;
    int fourth;

    @Tested
    StoreFiles storeFiles;

    @Injectable
    FileSystemFactory fileSystemFactory;

    @Mocked
    OracleConnection conn;

    @Before public void initMocks() {
       MockitoAnnotations.initMocks(StoreFiles.class);
    }

    @BeforeClass()
    public void init() {
        first= "test";
        second= "testRoot";
        third= Instant.now();
        fourth= 1;
    }

    @Test
    public void testRetry(@Mocked FileSystemFactory fileSystemFactory,
                          @Mocked StructDescriptor dbDataRecDesc,
                          @Mocked ArrayDescriptor dbDataTabDesc) throws CustomException {

        StoreFiles files = mock(StoreFiles.class);

        files.storeFiles(conn, first, second, third, fourth);

        Mockito.verify(files, times(2)).storeFiles(conn, first, second, third, fourth);
    }
}

目前正在使用jmockit、testng和mockito。我只需要确保在抛出
CustomException
时再次调用
storeFile
方法。如果我使用
storeFiles
对象而不是
files
对象,则会抛出我想要的异常。如果我像这里写的那样运行测试,我会得到一个错误,
storeFiles
方法只被调用了一次,指的是我在测试方法中显式调用它的地方。这两种方法都导致我正在测试的方法无法重试

这很难测试,因为
StoreFiles::storeFile
方法执行两件事:存储文件和执行重试逻辑。您可以更改您的设计,使其更易于测试。我的建议是:

首先,从
StoreFiles::storeFile
中删除重试逻辑,并将其提取到另一个具有该职责的类:

public class Retry {

    public void exec(Runnable r) {
        try {
            r.run();
        } catch (CustomException e) {
            r.run();
        }
    }

}
现在,您可以这样编写重试逻辑测试:

public class RetryTest {

    //fields and init method

    @Test
    public void test() {
        Retry retry = new Retry();
        Mockito
            .doThrow(new CustomException()) //first StoreFiles::storeFile call, throws exeption
            .doNothing() //second StoreFiles::storeFile call, does nothing
            .when(storeFiles).storeFile(conn, first, second, third, fourth);

        retry.exec(() -> storeFiles.storeFile(conn, first, second, third, fourth));

        //verify StoreFiles::storeFile is called twice
        Mockito.verify(storeFiles, Mockito.times(2)).storeFile(conn, first, second, third, fourth);
    }

}
(我假设CustomException是非检查异常)

另一个解决方案是实现decorator模式并构建
RetryStoreFiles

public class RetryStoreFiles implements StoreFiles {

    private final StoreFiles decorated;

    public RetryStoreFiles(StoreFiles decorated) {
        this.decorated = decorated;
    }

    @Override
    public void storeFile(conn: Conn, first: First, second: Second, third: Third, fourth: Fourth) {
        try {
            decorated.storeFile(conn, first, second, third, fourth);
        } catch (CustomException e) {
            decorated.storeFile(conn, first, second, third, fourth);
        }
    }

}

我更喜欢第二种解决方案。我认为它比第一个更具有语义和面向对象性。

唯一的问题是,在整个应用程序中都使用了用于重试方法的注释,而在使用该注释的地方进行重构会导致太多的头痛,并且重构和测试会花费太多的时间。