From 9e46061aa683b47d3fadc0ee809d83764240fe22 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 1 Jun 2021 14:04:35 +0200 Subject: [PATCH] Allow to chain ContextConsumer implementations Closes gh-26723 --- .../test/context/runner/ContextConsumer.java | 19 ++++- .../context/runner/ContextConsumerTests.java | 77 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ContextConsumerTests.java diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ContextConsumer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ContextConsumer.java index 3970a438d2..5bc5511a03 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ContextConsumer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ContextConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.boot.test.context.runner; import org.springframework.context.ApplicationContext; +import org.springframework.util.Assert; /** * Callback interface used to process an {@link ApplicationContext} with the ability to @@ -38,4 +39,20 @@ public interface ContextConsumer { */ void accept(C context) throws Throwable; + /** + * Returns a composed {@code ContextConsumer} that performs, in sequence, this + * operation followed by the {@code after} operation. + * @param after the operation to perform after this operation + * @return a composed {@code ContextConsumer} that performs in sequence this operation + * followed by the {@code after} operation + * @since 2.6.0 + */ + default ContextConsumer andThen(ContextConsumer after) { + Assert.notNull(after, "After must not be null"); + return (context) -> { + accept(context); + after.accept(context); + }; + } + } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ContextConsumerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ContextConsumerTests.java new file mode 100644 index 0000000000..62bd0b5810 --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/runner/ContextConsumerTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.context.runner; + +import java.util.function.IntPredicate; + +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; + +import org.springframework.context.ApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Tests for {@link ContextConsumer}. + * + * @author Stephane Nicoll + */ +class ContextConsumerTests { + + @Test + void andThenInvokeInOrder() throws Throwable { + IntPredicate predicate = mock(IntPredicate.class); + given(predicate.test(42)).willReturn(true); + given(predicate.test(24)).willReturn(false); + ContextConsumer firstConsumer = (context) -> assertThat(predicate.test(42)).isTrue(); + ContextConsumer secondConsumer = (context) -> assertThat(predicate.test(24)).isFalse(); + firstConsumer.andThen(secondConsumer).accept(mock(ApplicationContext.class)); + InOrder ordered = inOrder(predicate); + ordered.verify(predicate).test(42); + ordered.verify(predicate).test(24); + ordered.verifyNoMoreInteractions(); + } + + @Test + void andThenNoInvokedIfThisFails() { + IntPredicate predicate = mock(IntPredicate.class); + given(predicate.test(42)).willReturn(true); + given(predicate.test(24)).willReturn(false); + ContextConsumer firstConsumer = (context) -> assertThat(predicate.test(42)).isFalse(); + ContextConsumer secondConsumer = (context) -> assertThat(predicate.test(24)).isFalse(); + assertThatThrownBy(() -> firstConsumer.andThen(secondConsumer).accept(mock(ApplicationContext.class))) + .isInstanceOf(AssertionError.class); + verify(predicate).test(42); + verifyNoMoreInteractions(predicate); + } + + @Test + void andThenWithNull() { + ContextConsumer consumer = (context) -> { + }; + assertThatIllegalArgumentException().isThrownBy(() -> consumer.andThen(null)) + .withMessage("After must not be null"); + } + +}