Project Reactor - использование defer (), чтобы сделать метод повторным - PullRequest
0 голосов
/ 27 апреля 2020

Недавно я попытался протестировать поведение повторных попыток в модульных тестах, настроив ложную зависимость, чтобы она возвращала несколько Mono.error () перед возвратом успешного завершения mono.just () в конце:

@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 не выполняется путем повторного вызова метода в течение определенного времени c, но путем однократного вызова метода, получения издателя и повторной подписки на него снова и снова.

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 */);
    }
}

Поскольку мы хотим сказать, что каждый метод должен быть «повторяющимся», значит ли это, что нам нужно всегда использовать defer(...)?

Или я что-то неправильно понимаю?

1 Ответ

0 голосов
/ 27 апреля 2020

Мне следовало бы подумать об этом.

Более простой способ вместо того, чтобы сделать все методы изначально "повторяющимися", состоит в том, чтобы обернуть издателя defer перед присоединением оператора retryWhen.

До:

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));
    }
}

Теперь нам не нужно говорить, что все методы должны быть изначально "повторяемыми", но чтобы всегда переносить повторный издатель с defer.

...