NPE на FilePart макет в Spring WebFlux в реакторе.core.publisher.MonoWhen $ WhenCoordinator - PullRequest
0 голосов
/ 08 июня 2018

Буду признателен за любые подсказки по следующей проблеме, с которой я столкнулся.При модульном тестировании метода службы многоэтапной загрузки файлов в приложении Spring Reactive WebFlux я получаю NPE для reactor.core.publisher.MonoWhen$WhenCoordinator следующим образом

java.lang.NullPointerException
    at reactor.core.publisher.MonoWhen$WhenCoordinator.subscribe(MonoWhen.java:149) 

Полный журнал также указан ниже.

Тест:

@RunWith(SpringRunner.class)
@SpringBootTest
public class FileServiceTest2 {


@MockBean
private UploadedImageRepository uploadedImageRepository;

...

       @Test
        public void assembleImageTest() {
            UploadedImage ui1 = new UploadedImage("1", "ui1.png");
            UploadedImage ui2 = new UploadedImage("2", "ui2.png");

            FilePart filePart1 = mock(FilePart.class);
            FilePart filePart2 = mock(FilePart.class);

            given(this.uploadedImageRepository.save(ui1))
                    .willReturn(Mono.just(ui1));
            given(this.uploadedImageRepository.save(ui2))
                    .willReturn(Mono.just(ui2));
            given(this.uploadedImageRepository.findAll())
                    .willReturn(Flux.just(ui1, ui2));

            given(filePart1.filename())
                    .willReturn(ui1.getImageName());
            given(filePart1.transferTo(any()))
                    .willReturn(Mono.empty());
            given(filePart2.filename())
                    .willReturn(ui2.getImageName());
            given(filePart2.transferTo(any()))
                    .willReturn(Mono.empty());

            Flux<FilePart> files = Flux.just(filePart1, filePart2);

            StepVerifier.create(this.uploadService.createFile(files))
                    .verifyComplete();
        }

Тестируется:

@Service
public class UploadService {

Mono<Void> createFile(Flux<FilePart> fileParts) {
        return fileParts.flatMap(part -> {
            Mono<UploadedImage> savedToDBImage = this.uploadedImageRepository.save(
                    new UploadedImage(UUID.randomUUID().toString(), part.filename()))
                    .log("createFile-fileSavedToDB"); // NPE!

            Mono<Void> copiedFile = Mono.just(Paths.get(UPLOAD_URL, part.filename()).toFile())
                    .log("createFile-pathAssembled")
                    .doOnNext(destinationFile -> {
                        try {
                            destinationFile.createNewFile();
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    })
                    .log("createFile-fileAssembled")
                    .flatMap(part::transferTo)
                    .log("createFile-fileCopied");

            return Mono.when(savedToDBImage, copiedFile)
                    .log("createFile-monoWhen");
        })
                .log("createFile-flatMap")
                .then()
                .log("createFile-done");
    }

UploadedImage класс (с Ломбоком):

@Data
@RequiredArgsConstructor
@NoArgsConstructor
public class UploadedImage {

        @NonNull private String id;
        @NonNull private String imageName;

}

Реактивный репозиторий SpringData:

@Repository
public interface UploadedImageRepository extends ReactiveCrudRepository<UploadedImage, String> {

}

Логи следующие:

java.lang.NullPointerException
    at reactor.core.publisher.MonoWhen$WhenCoordinator.subscribe(MonoWhen.java:149)
    at reactor.core.publisher.MonoWhen.subscribe(MonoWhen.java:99)
    at reactor.core.publisher.MonoOnAssembly.subscribe(MonoOnAssembly.java:76)
    at reactor.core.publisher.MonoLogFuseable.subscribe(MonoLogFuseable.java:53)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3080)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:372)
    at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:198)
    at reactor.core.publisher.FluxArray$ArraySubscription.slowPath(FluxArray.java:118)
    at reactor.core.publisher.FluxArray$ArraySubscription.request(FluxArray.java:91)
    at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:138)
    at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:332)
    at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:172)
    at reactor.core.publisher.FluxArray.subscribe(FluxArray.java:53)
    at reactor.core.publisher.FluxArray.subscribe(FluxArray.java:59)
    at reactor.core.publisher.FluxLogFuseable.subscribe(FluxLogFuseable.java:53)
    at reactor.core.publisher.FluxFlatMap.subscribe(FluxFlatMap.java:97)
    at reactor.core.publisher.FluxLog.subscribe(FluxLog.java:50)
    at reactor.core.publisher.MonoIgnoreElements.subscribe(MonoIgnoreElements.java:37)
    at reactor.core.publisher.MonoLog.subscribe(MonoLog.java:51)
    at reactor.core.publisher.Mono.subscribe(Mono.java:3080)
    at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.verify(DefaultStepVerifierBuilder.java:728)
    at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.verify(DefaultStepVerifierBuilder.java:700)
    at reactor.test.DefaultStepVerifierBuilder.verifyComplete(DefaultStepVerifierBuilder.java:566)
    at pb.sl.UploadService.createFile(FileServiceTest2.java:112)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

1 Ответ

0 голосов
/ 08 июня 2018

Если присмотреться, я думаю, что вы не дразните свой репозиторий, как ожидалось, и в вашем тесте вызов репозитория возвращает ноль.

Вы используете не средство сопоставления аргументов Mockito, а конкретный экземпляр дляаргумент.Позже в реализации вашей службы этот метод вызывается, но с другим экземпляром.Я предполагаю, что Mockito использует equals, чтобы проверить, соответствует ли данное совпадение этому ложному вызову - может быть, ваш UploadedImage может быть улучшен в этом отношении?

given(this.uploadedImageRepository.save(ui1)) должно быть:

given(this.uploadedImageRepository.save(any()))
.willAnswer(invocation -> Mono.just(invocation.getArgument(0)))
...