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);
}
}
}
我更喜欢第二种解决方案。我认为它比第一个更具有语义和面向对象性。唯一的问题是,在整个应用程序中都使用了用于重试方法的注释,而在使用该注释的地方进行重构会导致太多的头痛,并且重构和测试会花费太多的时间。