From 81c7d808954ce2caeac096a995629fbbefbb7d94 Mon Sep 17 00:00:00 2001 From: Roger Floriano <31597636+petruki@users.noreply.github.com> Date: Sun, 21 Apr 2024 21:55:21 -0700 Subject: [PATCH] Added support to multiple SwitcherMocks (#283) * Added support to multiple SwitcherMocks * chore: refactored ClientLocalService former singleton * chore: Renamed array of switchers --- README.md | 1 - .../switcherapi/client/SwitcherMock.java | 16 ++-- .../client/SwitcherMockExtension.java | 92 +++++++++++++++++++ .../client/SwitcherMockRunner.java | 55 ----------- .../switcherapi/client/SwitcherMockValue.java | 9 ++ .../service/local/ClientLocalService.java | 10 +- .../service/local/SwitcherLocalService.java | 7 +- .../com/github/switcherapi/Switchers.java | 3 + .../client/SwitcherBypassTest.java | 42 ++++++--- src/test/resources/snapshot/fixture2.json | 6 ++ 10 files changed, 153 insertions(+), 88 deletions(-) create mode 100644 src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java delete mode 100644 src/main/java/com/github/switcherapi/client/SwitcherMockRunner.java create mode 100644 src/main/java/com/github/switcherapi/client/SwitcherMockValue.java diff --git a/README.md b/README.md index 06e4a80d..3b2998d5 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,6 @@ Predefine Switchers result outside your test methods via Parameterized Test.
It encapsulates the test and makes sure that the Switcher returns to its original state after concluding the test. ```java -@ParameterizedTest @SwitcherMock(key = MY_SWITCHER, result = true) void testMyFeature() { assertTrue(instance.myFeature()); diff --git a/src/main/java/com/github/switcherapi/client/SwitcherMock.java b/src/main/java/com/github/switcherapi/client/SwitcherMock.java index b9b3e3eb..9ce51e2a 100644 --- a/src/main/java/com/github/switcherapi/client/SwitcherMock.java +++ b/src/main/java/com/github/switcherapi/client/SwitcherMock.java @@ -6,6 +6,7 @@ import java.lang.annotation.Target; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; /** @@ -18,12 +19,15 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@ArgumentsSource(SwitcherMockRunner.class) -@ExtendWith(SwitcherMockRunner.class) +@ArgumentsSource(SwitcherMockExtension.class) +@ExtendWith(SwitcherMockExtension.class) +@ParameterizedTest public @interface SwitcherMock { - - String key(); - - boolean result(); + + String key() default ""; + + boolean result() default true; + + SwitcherMockValue[] switchers() default {}; } diff --git a/src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java b/src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java new file mode 100644 index 00000000..6d26a8f6 --- /dev/null +++ b/src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java @@ -0,0 +1,92 @@ +package com.github.switcherapi.client; + +import org.apache.commons.lang3.ArrayUtils; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.support.AnnotationConsumer; + +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * This extension implements a Parameterized Test that can mock Switcher results and + * reset after its conclusion + * + * @author Roger Floriano (petruki) + */ +class SwitcherMockExtension implements AfterTestExecutionCallback, + ArgumentsProvider, AnnotationConsumer { + + private static final String STORE_KEYS = "store_keys"; + private static final String STORE_KEY = "store_key"; + + private String key; + + private boolean result; + + private SwitcherMockValue[] switchers; + + @Override + public void accept(SwitcherMock switcherTester) { + this.key = switcherTester.key(); + this.result = switcherTester.result(); + this.switchers = switcherTester.switchers(); + } + + @Override + public Stream provideArguments(ExtensionContext context) { + if (ArrayUtils.isNotEmpty(switchers)) { + return provideMultipleArguments(context); + } + + return provideSingleArgument(context); + } + + @Override + public void afterTestExecution(ExtensionContext context) { + Optional parent = context.getParent(); + if (parent.isPresent()) { + Store store = getStore(parent.get()); + String[] keys = store.remove(STORE_KEYS, String[].class); + + if (ArrayUtils.isNotEmpty(keys)) { + for (String keyStored : keys) { + SwitcherExecutor.forget(keyStored); + } + } else { + String switcherKey = store.remove(STORE_KEY, String.class); + SwitcherExecutor.forget(switcherKey); + } + } + } + + private Stream provideMultipleArguments(ExtensionContext context) { + String[] keys = Arrays.stream(switchers) + .map(SwitcherMockValue::key) + .toArray(String[]::new); + + for (SwitcherMockValue value : switchers) { + SwitcherExecutor.assume(value.key(), value.result()); + } + + getStore(context).put(STORE_KEYS, keys); + return Stream.of(Arguments.of(keys, null)); + } + + private Stream provideSingleArgument(ExtensionContext context) { + SwitcherExecutor.assume(key, result); + getStore(context).put(STORE_KEY, key); + + return Stream.of(Arguments.of(key, null)); + } + + private Store getStore(ExtensionContext context) { + return context.getStore(Namespace.create(getClass(), context)); + } + +} diff --git a/src/main/java/com/github/switcherapi/client/SwitcherMockRunner.java b/src/main/java/com/github/switcherapi/client/SwitcherMockRunner.java deleted file mode 100644 index 2316af13..00000000 --- a/src/main/java/com/github/switcherapi/client/SwitcherMockRunner.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.github.switcherapi.client; - -import java.util.Optional; -import java.util.stream.Stream; - -import org.junit.jupiter.api.extension.AfterTestExecutionCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.support.AnnotationConsumer; - -import com.github.switcherapi.client.model.Switcher; - -/** - * This runner implements a Parameterized Test that can mock the Switcher result and - * reset after its conclusion - * - * @author Roger Floriano (petruki) - */ -class SwitcherMockRunner implements AfterTestExecutionCallback, - ArgumentsProvider, AnnotationConsumer { - - private String key; - - private boolean result; - - @Override - public void accept(SwitcherMock switcherTester) { - this.key = switcherTester.key(); - this.result = switcherTester.result(); - } - - @Override - public Stream provideArguments(ExtensionContext context) { - SwitcherExecutor.assume(key, result); - getStore(context).put(Switcher.KEY, key); - return Stream.of(Arguments.of(key, null)); - } - - @Override - public void afterTestExecution(ExtensionContext context) { - Optional parent = context.getParent(); - if (parent.isPresent()) { - String switcherKey = getStore(parent.get()).remove(Switcher.KEY, String.class); - SwitcherExecutor.forget(switcherKey); - } - } - - private Store getStore(ExtensionContext context) { - return context.getStore(Namespace.create(getClass(), context)); - } - -} diff --git a/src/main/java/com/github/switcherapi/client/SwitcherMockValue.java b/src/main/java/com/github/switcherapi/client/SwitcherMockValue.java new file mode 100644 index 00000000..0a5e4fd7 --- /dev/null +++ b/src/main/java/com/github/switcherapi/client/SwitcherMockValue.java @@ -0,0 +1,9 @@ +package com.github.switcherapi.client; + +public @interface SwitcherMockValue { + + String key(); + + boolean result() default true; + +} diff --git a/src/main/java/com/github/switcherapi/client/service/local/ClientLocalService.java b/src/main/java/com/github/switcherapi/client/service/local/ClientLocalService.java index bb6ac1b3..0234a553 100644 --- a/src/main/java/com/github/switcherapi/client/service/local/ClientLocalService.java +++ b/src/main/java/com/github/switcherapi/client/service/local/ClientLocalService.java @@ -39,20 +39,12 @@ public class ClientLocalService { private static final String STRATEGY_FAIL_NO_INPUT_PATTERN = "Strategy %s did not receive any input"; private static final String CRITERIA_SUCCESS = "Success"; - private static ClientLocalService instance; private final ValidatorService validatorService; - private ClientLocalService() { + public ClientLocalService() { this.validatorService = new ValidatorService(); } - public static ClientLocalService getInstance() { - if (instance == null) { - instance = new ClientLocalService(); - } - return instance; - } - public List checkSwitchers(final Set switchers, final Domain domain) { List notFound = new ArrayList<>(); diff --git a/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java b/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java index 27db3702..7f4994ad 100644 --- a/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java +++ b/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java @@ -32,11 +32,14 @@ public class SwitcherLocalService extends SwitcherExecutor { private static final Logger logger = LogManager.getLogger(SwitcherLocalService.class); private final ClientRemote clientRemote; + + private final ClientLocalService clientLocalService; private Domain domain; public SwitcherLocalService() { this.clientRemote = new ClientRemoteService(); + this.clientLocalService = new ClientLocalService(); this.init(); } @@ -72,7 +75,7 @@ public CriteriaResponse executeCriteria(final Switcher switcher) { response = this.clientRemote.executeCriteria(switcher); SwitcherUtils.debug(logger, "[Remote] response: {}", response); } else { - response = ClientLocalService.getInstance().executeCriteria(switcher, this.domain); + response = this.clientLocalService.executeCriteria(switcher, this.domain); SwitcherUtils.debug(logger, "[Local] response: {}", response); } @@ -97,7 +100,7 @@ public void checkSwitchers(final Set switchers) { throw new SwitcherContextException("Snapshot not loaded"); } - final List response = ClientLocalService.getInstance().checkSwitchers(switchers, this.domain); + final List response = this.clientLocalService.checkSwitchers(switchers, this.domain); if (!response.isEmpty()) { throw new SwitchersValidationException(response.toString()); } diff --git a/src/test/java/com/github/switcherapi/Switchers.java b/src/test/java/com/github/switcherapi/Switchers.java index e48069e8..5789b8df 100644 --- a/src/test/java/com/github/switcherapi/Switchers.java +++ b/src/test/java/com/github/switcherapi/Switchers.java @@ -14,6 +14,9 @@ public class Switchers extends SwitcherContext { @SwitcherKey public static final String USECASE111 = "USECASE111"; + @SwitcherKey + public static final String USECASE112 = "USECASE112"; + @SwitcherKey public static final String USECASE12 = "USECASE12"; diff --git a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java index 8abe4d35..0ba19ecd 100644 --- a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java +++ b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java @@ -1,20 +1,17 @@ package com.github.switcherapi.client; -import static com.github.switcherapi.Switchers.USECASE11; -import static com.github.switcherapi.Switchers.USECASE111; -import static com.github.switcherapi.client.SwitcherContext.getSwitcher; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.nio.file.Paths; - +import com.github.switcherapi.client.model.Switcher; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import com.github.switcherapi.client.model.Switcher; +import java.nio.file.Paths; + +import static com.github.switcherapi.Switchers.*; +import static com.github.switcherapi.client.SwitcherContext.getSwitcher; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class SwitcherBypassTest { @@ -93,8 +90,7 @@ void shouldReturnFalse_afterAssumingItsTrue() { SwitcherExecutor.forget(USECASE111); assertFalse(switcher.isItOn()); } - - @ParameterizedTest + @SwitcherMock(key = USECASE111, result = false) void shouldReturnFalse_usingParametrizedTest() { //given @@ -105,9 +101,8 @@ void shouldReturnFalse_usingParametrizedTest() { Switcher switcher = getSwitcher(USECASE111); assertFalse(switcher.isItOn()); } - - @ParameterizedTest - @SwitcherMock(key = USECASE111, result = true) + + @SwitcherMock(key = USECASE111) void shouldReturnTrue_usingParametrizedTest() { //given SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2)); @@ -118,4 +113,21 @@ void shouldReturnTrue_usingParametrizedTest() { assertTrue(switcher.isItOn()); } + @SwitcherMock(switchers = { + @SwitcherMockValue(key = USECASE111), + @SwitcherMockValue(key = USECASE112) + }) + void shouldReturnTrue_usingParametrizedTestWithMultipleValues() { + //given + SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2)); + SwitcherContext.initializeClient(); + + //test + Switcher switcher = getSwitcher(USECASE111); + assertTrue(switcher.isItOn()); + + switcher = getSwitcher(USECASE112); + assertTrue(switcher.isItOn()); + } + } diff --git a/src/test/resources/snapshot/fixture2.json b/src/test/resources/snapshot/fixture2.json index 194bb5bc..323c0fe5 100644 --- a/src/test/resources/snapshot/fixture2.json +++ b/src/test/resources/snapshot/fixture2.json @@ -15,6 +15,12 @@ "description": "Simple test - Domain disabled", "activated": true, "components": ["switcher-client"] + }, + { + "key": "USECASE112", + "description": "Simple test - Domain disabled", + "activated": true, + "components": ["switcher-client"] } ] }