Project Reactor - 使用 defer() 使方法可重试
Project Reactor - the usage of defer() to make method retry-able
最近我试图通过在 return 成功 mono.just 之前设置对 return 多个 Mono.error() 的模拟依赖来测试单元测试中的重试行为() 最后:
@Mock
Dependency dependency;
@InjectMocks
ClassUnderTest classUnderTest;
@Test
void someTest() {
final Object object = new Object();
when(dependency.method(anyString()))
.thenReturn(Mono.error(new Exception()))
.thenReturn(Mono.error(new Exception()))
.thenReturn(Mono.error(new Exception()))
.thenReturn(Mono.just(object));
StepVerifier.create(classUnderTest.method("abc"))
.expectNext(object)
.verifyComplete();
verify(dependency, times(4)).method("abc");
}
上面的设置是行不通的,后来我发现,Reactor中的重试不是通过调用特定时间的方法来完成的,而是调用一次方法,获取发布者,然后重新订阅一次又一次。
class ClassUnderTest {
private Dependency dependency;
public Mono<Object> method(final String str) {
return this.dependency.method(str).retryWhen(Retry.max(3));
}
}
并且重新订阅 将不会 工作,如果 Dependency#method
实现为:
class Dependency {
private OtherDependency otherDependency;
public Mono<Object> method(final String str) {
return this.otherDependency.get(str).map(/* some mapping logic */);
}
}
Dependency#method
不能对OtherDependency#get
是否延期做太多假设。因此,Dependency
需要:
class Dependency {
private OtherDependency otherDependency;
public Mono<Object> method(final String str) {
return Mono.defer(() -> this.otherDependency.get(str)).map(/* some mapping logic */);
}
}
既然我们想说每个方法都应该是"retry-able",那是否意味着我们需要总是使用defer(...)
?
还是我理解有误?
早该考虑一下
更简单的方法是在附加 retryWhen
运算符之前用 defer
包装发布者,而不是使所有方法都是本机 "retry-able"。
之前:
class ClassUnderTest {
private Dependency dependency;
public Mono<Object> method(final String str) {
return dependency.method(str).retryWhen(Retry.max(3));
}
}
之后:
class ClassUnderTest {
private Dependency dependency;
public Mono<Object> method(final String str) {
return Mono.defer(() -> dependency.method(str)).retryWhen(Retry.max(3));
}
}
现在,我们不必说所有方法都应该是原生的 "retry-able",而是要始终用 defer
.
包装要重试的发布者
最近我试图通过在 return 成功 mono.just 之前设置对 return 多个 Mono.error() 的模拟依赖来测试单元测试中的重试行为() 最后:
@Mock
Dependency dependency;
@InjectMocks
ClassUnderTest classUnderTest;
@Test
void someTest() {
final Object object = new Object();
when(dependency.method(anyString()))
.thenReturn(Mono.error(new Exception()))
.thenReturn(Mono.error(new Exception()))
.thenReturn(Mono.error(new Exception()))
.thenReturn(Mono.just(object));
StepVerifier.create(classUnderTest.method("abc"))
.expectNext(object)
.verifyComplete();
verify(dependency, times(4)).method("abc");
}
上面的设置是行不通的,后来我发现,Reactor中的重试不是通过调用特定时间的方法来完成的,而是调用一次方法,获取发布者,然后重新订阅一次又一次。
class ClassUnderTest {
private Dependency dependency;
public Mono<Object> method(final String str) {
return this.dependency.method(str).retryWhen(Retry.max(3));
}
}
并且重新订阅 将不会 工作,如果 Dependency#method
实现为:
class Dependency {
private OtherDependency otherDependency;
public Mono<Object> method(final String str) {
return this.otherDependency.get(str).map(/* some mapping logic */);
}
}
Dependency#method
不能对OtherDependency#get
是否延期做太多假设。因此,Dependency
需要:
class Dependency {
private OtherDependency otherDependency;
public Mono<Object> method(final String str) {
return Mono.defer(() -> this.otherDependency.get(str)).map(/* some mapping logic */);
}
}
既然我们想说每个方法都应该是"retry-able",那是否意味着我们需要总是使用defer(...)
?
还是我理解有误?
早该考虑一下
更简单的方法是在附加 retryWhen
运算符之前用 defer
包装发布者,而不是使所有方法都是本机 "retry-able"。
之前:
class ClassUnderTest {
private Dependency dependency;
public Mono<Object> method(final String str) {
return dependency.method(str).retryWhen(Retry.max(3));
}
}
之后:
class ClassUnderTest {
private Dependency dependency;
public Mono<Object> method(final String str) {
return Mono.defer(() -> dependency.method(str)).retryWhen(Retry.max(3));
}
}
现在,我们不必说所有方法都应该是原生的 "retry-able",而是要始终用 defer
.