com.squareup.okhttp3
@@ -173,19 +173,19 @@
com.fasterxml.jackson.core
jackson-annotations
- 2.16.1
+ 2.17.0
com.fasterxml.jackson.core
jackson-databind
- 2.16.1
+ 2.17.0
com.fasterxml.jackson.module
jackson-module-jakarta-xmlbind-annotations
- 2.16.1
+ 2.17.0
diff --git a/src/main/java/com/github/switcherapi/client/SwitcherKey.java b/src/main/java/com/github/switcherapi/client/SwitcherKey.java
index 49e2bc16..2e613597 100644
--- a/src/main/java/com/github/switcherapi/client/SwitcherKey.java
+++ b/src/main/java/com/github/switcherapi/client/SwitcherKey.java
@@ -1,5 +1,7 @@
package com.github.switcherapi.client;
+import com.github.switcherapi.client.test.SwitcherTest;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -15,7 +17,7 @@
*
*
* The attribute name is used to be sent to the API and its value
- * is used to work with {@link SwitcherMock}
+ * is used to work with {@link SwitcherTest}
*
* @author Roger Floriano (petruki)
*/
diff --git a/src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java b/src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java
deleted file mode 100644
index 6d26a8f6..00000000
--- a/src/main/java/com/github/switcherapi/client/SwitcherMockExtension.java
+++ /dev/null
@@ -1,92 +0,0 @@
-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 extends Arguments> 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 extends Arguments> 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 extends Arguments> 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/SwitcherMockValue.java b/src/main/java/com/github/switcherapi/client/SwitcherMockValue.java
deleted file mode 100644
index 0a5e4fd7..00000000
--- a/src/main/java/com/github/switcherapi/client/SwitcherMockValue.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.github.switcherapi.client;
-
-public @interface SwitcherMockValue {
-
- String key();
-
- boolean result() default true;
-
-}
diff --git a/src/main/java/com/github/switcherapi/client/SwitcherMock.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTest.java
similarity index 55%
rename from src/main/java/com/github/switcherapi/client/SwitcherMock.java
rename to src/main/java/com/github/switcherapi/client/test/SwitcherTest.java
index 9ce51e2a..3fe57f5d 100644
--- a/src/main/java/com/github/switcherapi/client/SwitcherMock.java
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTest.java
@@ -1,33 +1,33 @@
-package com.github.switcherapi.client;
+package com.github.switcherapi.client.test;
+
+import org.junit.jupiter.api.TestTemplate;
+import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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;
-
/**
* Annotate test cases with the Switcher Key and the expected result.
*
*
- * Requires JUnit 5 Jupiter @ParameterizedTest
+ * Requires JUnit 5 Jupiter
*
* @author Roger Floriano (petruki)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
-@ArgumentsSource(SwitcherMockExtension.class)
-@ExtendWith(SwitcherMockExtension.class)
-@ParameterizedTest
-public @interface SwitcherMock {
+@ExtendWith(SwitcherTestExtension.class)
+@TestTemplate
+public @interface SwitcherTest {
String key() default "";
boolean result() default true;
- SwitcherMockValue[] switchers() default {};
+ boolean abTest() default false;
+
+ SwitcherTestValue[] switchers() default {};
}
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java
new file mode 100644
index 00000000..afee6203
--- /dev/null
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java
@@ -0,0 +1,96 @@
+package com.github.switcherapi.client.test;
+
+import com.github.switcherapi.client.SwitcherExecutor;
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.jupiter.api.extension.*;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+/**
+ * This extension implements test template, before and after routines to mock Switcher results and
+ * reset after its conclusion
+ *
+ * @author Roger Floriano (petruki)
+ */
+class SwitcherTestExtension implements TestTemplateInvocationContextProvider,
+ AfterTestExecutionCallback, BeforeTestExecutionCallback {
+
+ private static final String STORE_KEYS = "mock.keys";
+ private static final String STORE_KEY = "mock.key";
+
+ private boolean abTest;
+
+ @Override
+ public boolean supportsTestTemplate(ExtensionContext context) {
+ return context.getRequiredTestMethod().isAnnotationPresent(SwitcherTest.class);
+ }
+
+ @Override
+ public Stream provideTestTemplateInvocationContexts(ExtensionContext context) {
+ SwitcherTest switcherTest = context.getRequiredTestMethod().getAnnotation(SwitcherTest.class);
+
+ if (switcherTest.abTest()) {
+ final SwitcherTestTemplate templateA = new SwitcherTestTemplate(switcherTest, true);
+ final SwitcherTestTemplate templateB = new SwitcherTestTemplate(switcherTest);
+ return Stream.of(templateA, templateB);
+ }
+
+ final SwitcherTestTemplate template = new SwitcherTestTemplate(switcherTest);
+ return Stream.of(template);
+ }
+
+ @Override
+ public void beforeTestExecution(ExtensionContext context) {
+ SwitcherTest switcherTest = context.getRequiredTestMethod().getAnnotation(SwitcherTest.class);
+
+ if (switcherTest.abTest()) {
+ abTest = !abTest;
+ }
+
+ if (ArrayUtils.isNotEmpty(switcherTest.switchers())) {
+ mockMultipleSwitchers(context, switcherTest, abTest);
+ } else {
+ mockSingleSwitcher(context, switcherTest, abTest);
+ }
+ }
+
+ @Override
+ public void afterTestExecution(ExtensionContext context) {
+ Store store = getStore(context);
+ 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 void mockMultipleSwitchers(ExtensionContext context, SwitcherTest switcherTest, boolean inverted) {
+ String[] keys = Arrays.stream(switcherTest.switchers())
+ .map(SwitcherTestValue::key)
+ .toArray(String[]::new);
+
+ for (SwitcherTestValue value : switcherTest.switchers()) {
+ SwitcherExecutor.assume(value.key(), inverted != value.result());
+ }
+
+ getStore(context).put(STORE_KEYS, keys);
+ }
+
+ private void mockSingleSwitcher(ExtensionContext context, SwitcherTest switcherTest, boolean inverted) {
+ SwitcherExecutor.assume(switcherTest.key(), inverted != switcherTest.result());
+ getStore(context).put(STORE_KEY, switcherTest.key());
+ }
+
+ private Store getStore(ExtensionContext context) {
+ return context.getStore(Namespace.create(getClass(), context));
+ }
+
+}
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java
new file mode 100644
index 00000000..b8e9ddfa
--- /dev/null
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java
@@ -0,0 +1,38 @@
+package com.github.switcherapi.client.test;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
+
+import java.util.Arrays;
+
+class SwitcherTestTemplate implements TestTemplateInvocationContext {
+
+ private static final String DISPLAY_NAME_TEMPLATE = "With %s as %s";
+
+ private final SwitcherTest switcherTest;
+
+ private boolean inverted;
+
+ SwitcherTestTemplate(SwitcherTest switcherTest, boolean inverted) {
+ this.switcherTest = switcherTest;
+ this.inverted = inverted;
+ }
+
+ SwitcherTestTemplate(SwitcherTest switcherTest) {
+ this.switcherTest = switcherTest;
+ }
+
+ @Override
+ public String getDisplayName(int invocationIndex) {
+ SwitcherTestValue[] switcherTestValues = switcherTest.switchers();
+
+ if (ArrayUtils.isNotEmpty(switcherTestValues)) {
+ return String.join(", ", Arrays.toString(
+ Arrays.stream(switcherTestValues)
+ .map(value -> String.format(DISPLAY_NAME_TEMPLATE, value.key(), inverted != value.result()))
+ .toArray()));
+ }
+
+ return String.format(DISPLAY_NAME_TEMPLATE, switcherTest.key(), inverted != switcherTest.result());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java
new file mode 100644
index 00000000..a4c63c65
--- /dev/null
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java
@@ -0,0 +1,9 @@
+package com.github.switcherapi.client.test;
+
+public @interface SwitcherTestValue {
+
+ String key();
+
+ boolean result() default true;
+
+}
diff --git a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java
index 2eb42cba..8e3024f8 100644
--- a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java
+++ b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java
@@ -1,6 +1,8 @@
package com.github.switcherapi.client;
import com.github.switcherapi.client.model.Switcher;
+import com.github.switcherapi.client.test.SwitcherTest;
+import com.github.switcherapi.client.test.SwitcherTestValue;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -10,8 +12,7 @@
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;
+import static org.junit.jupiter.api.Assertions.*;
class SwitcherBypassTest {
@@ -91,7 +92,7 @@ void shouldReturnFalse_afterAssumingItsTrue() {
assertFalse(switcher.isItOn());
}
- @SwitcherMock(key = USECASE111, result = false)
+ @SwitcherTest(key = USECASE111, result = false)
void shouldReturnFalse_usingParametrizedTest() {
//given
SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2));
@@ -102,7 +103,7 @@ void shouldReturnFalse_usingParametrizedTest() {
assertFalse(switcher.isItOn());
}
- @SwitcherMock(key = USECASE111)
+ @SwitcherTest(key = USECASE111)
void shouldReturnTrue_usingParametrizedTest() {
//given
SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2));
@@ -113,9 +114,9 @@ void shouldReturnTrue_usingParametrizedTest() {
assertTrue(switcher.isItOn());
}
- @SwitcherMock(switchers = {
- @SwitcherMockValue(key = USECASE111),
- @SwitcherMockValue(key = USECASE112)
+ @SwitcherTest(switchers = {
+ @SwitcherTestValue(key = USECASE111),
+ @SwitcherTestValue(key = USECASE112)
})
void shouldReturnTrue_usingParametrizedTestWithMultipleValues() {
//given
@@ -130,4 +131,25 @@ void shouldReturnTrue_usingParametrizedTestWithMultipleValues() {
assertTrue(switcher.isItOn());
}
+ @SwitcherTest(key = USECASE111, abTest = true)
+ void shouldReturnSameResult_usingAbTest() {
+ assertEquals("Switcher key is " + USECASE111, workBothWay());
+ }
+
+ /**
+ * Fake scenario to test both ways of building a String.
+ * It is used to AB Test behavior when the same result is expected.
+ */
+ private String workBothWay() {
+ Switcher switcher = getSwitcher(USECASE111);
+
+ // Using String.format
+ if (switcher.isItOn()) {
+ return String.format("Switcher key is %s", USECASE111);
+ }
+
+ // Using String concatenation
+ return "Switcher key is " + USECASE111;
+ }
+
}