From b83e1820198a36dfe865be0bb5ca06edada42f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= <1000+aslakhellesoy@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:34:00 -0500 Subject: [PATCH 1/3] Add Support for Cucumber using @split[feature:treatment] tags (#247) * Add CucumberSplit * Add URL in JavaDoc * Improve the Cucumber example to be more realistic * disambiguation --- testing/pom.xml | 10 ++++ .../testing/cucumber/CucumberSplit.java | 51 +++++++++++++++++ .../testing/cucumber/CoffeeMachine.java | 44 +++++++++++++++ .../testing/cucumber/RunCucumberTest.java | 9 +++ .../io/split/client/testing/cucumber/SKU.java | 37 +++++++++++++ .../testing/cucumber/StepDefinitions.java | 55 +++++++++++++++++++ .../src/test/resources/cucumber.properties | 1 + .../testing/cucumber/MakeCoffee.feature | 25 +++++++++ 8 files changed, 232 insertions(+) create mode 100644 testing/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java create mode 100644 testing/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java create mode 100644 testing/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java create mode 100644 testing/src/test/java/io/split/client/testing/cucumber/SKU.java create mode 100644 testing/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java create mode 100644 testing/src/test/resources/cucumber.properties create mode 100644 testing/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature diff --git a/testing/pom.xml b/testing/pom.xml index bbad31d7f..d690ce04f 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -23,5 +23,15 @@ junit junit + + io.cucumber + cucumber-java + 6.10.4 + + + io.cucumber + cucumber-junit + 6.10.4 + diff --git a/testing/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java b/testing/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java new file mode 100644 index 000000000..716b51088 --- /dev/null +++ b/testing/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java @@ -0,0 +1,51 @@ +package io.split.client.testing.cucumber; + +import io.cucumber.java.Scenario; +import io.split.client.testing.SplitClientForTest; + +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

+ * Simple Cucumber plugin for Split. + *

+ *

+ * Cucumber scenarios annotated with {@code @split[feature:treatment]} tags can be used to + * configure a {@link SplitClientForTest} instance. + *

+ *

+ * To use it, define a Before Hook that invokes the {@link CucumberSplit#configureSplit(SplitClientForTest, Scenario)} + * method. Example: + *

+ * + *
+ * import io.cucumber.java.Before;
+ * import io.split.client.testing.SplitClientForTest;
+ *
+ * public class StepDefinitions {
+ *     private final SplitClientForTest splitClient = new SplitClientForTest();
+ *
+ *     @Before
+ *     public void configureSplit(Scenario scenario) {
+ *         CucumberSplit.configureSplit(splitClient, scenario);
+ *     }
+ * }
+ * 
+ */ +public class CucumberSplit { + private static final Pattern SPLIT_TAG_PATTERN = Pattern.compile("^@split\\[(.*):(.*)]"); + + public static void configureSplit(SplitClientForTest splitClient, Scenario scenario) { + Collection tags = scenario.getSourceTagNames(); + for (String tag : tags) { + Matcher matcher = SPLIT_TAG_PATTERN.matcher(tag); + if (matcher.matches()) { + String feature = matcher.group(1); + String treatment = matcher.group(2); + splitClient.registerTreatment(feature, treatment); + } + } + } +} diff --git a/testing/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java b/testing/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java new file mode 100644 index 000000000..2cfa2e913 --- /dev/null +++ b/testing/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java @@ -0,0 +1,44 @@ +package io.split.client.testing.cucumber; + +import io.split.client.SplitClient; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; + +/** + * A simple coffee machine that displays available drinks. It can offer an experimental cappuccino + * drink that is toggled on/off with Split. + */ +public class CoffeeMachine { + private final SplitClient splitClient; + private final String splitKey; + private double level; + + public CoffeeMachine(SplitClient splitClient, String splitKey) { + this.splitClient = splitClient; + this.splitKey = splitKey; + } + + /** + * Indicate how full the machine is + * + * @param level a number between 0 and 1 + */ + public void setLevel(double level) { + this.level = level; + } + + public List getAvailableDrinks() { + if(this.level == 0) return emptyList(); + + List availableDrinks = new ArrayList<>(); + availableDrinks.add(new SKU("filter coffee", 0.80)); + if ("on".equals(this.splitClient.getTreatment(splitKey, "cappuccino"))) { + availableDrinks.add(new SKU("cappuccino", 1.10)); + } + return unmodifiableList(availableDrinks); + } +} diff --git a/testing/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java b/testing/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java new file mode 100644 index 000000000..18a325ab7 --- /dev/null +++ b/testing/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java @@ -0,0 +1,9 @@ +package io.split.client.testing.cucumber; + +import io.cucumber.junit.Cucumber; +import org.junit.runner.RunWith; + +// This is the entry point for Cucumber, which runs all the .feature files in the same package +@RunWith(Cucumber.class) +public class RunCucumberTest { +} diff --git a/testing/src/test/java/io/split/client/testing/cucumber/SKU.java b/testing/src/test/java/io/split/client/testing/cucumber/SKU.java new file mode 100644 index 000000000..12e5a8cb1 --- /dev/null +++ b/testing/src/test/java/io/split/client/testing/cucumber/SKU.java @@ -0,0 +1,37 @@ +package io.split.client.testing.cucumber; + +import java.util.Objects; + +/** + * A simple Stock Keeping Unit (SKU). + */ +public class SKU { + private final String name; + private final double price; + + public SKU(String name, double price) { + this.name = name; + this.price = price; + } + + @Override + public String toString() { + return "SKU{" + + "name='" + name + '\'' + + ", price=" + price + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SKU sku = (SKU) o; + return Double.compare(sku.price, price) == 0 && name.equals(sku.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, price); + } +} diff --git a/testing/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java b/testing/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java new file mode 100644 index 000000000..59d3eae29 --- /dev/null +++ b/testing/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java @@ -0,0 +1,55 @@ +package io.split.client.testing.cucumber; + +import io.cucumber.java.Before; +import io.cucumber.java.DataTableType; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.split.client.testing.SplitClientForTest; + +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertEquals; + +public class StepDefinitions { + private final SplitClientForTest splitClient = new SplitClientForTest(); + private final CoffeeMachine coffeeMachine = new CoffeeMachine(splitClient, "arbitraryKey"); + + // Called by Cucumber to convert each row in the data table in the .feature file to a SKU object + @DataTableType + public SKU sku(Map entry) { + return new SKU( + entry.get("name"), + Double.parseDouble(entry.get("price")) + ); + } + + @Given("the machine is not empty") + public void the_machine_is_not_empty() { + coffeeMachine.setLevel(1.0); + } + + @Given("the machine is empty") + public void the_machine_is_empty() { + coffeeMachine.setLevel(0); + } + + @Then("the following drinks should be available:") + public void the_following_drinks_should_be_available(List expectedSKUs) { + List availableSKUs = coffeeMachine.getAvailableDrinks(); + assertEquals(expectedSKUs, availableSKUs); + } + + @Then("no drinks should be available") + public void no_drinks_should_be_available() { + List availableSKUs = coffeeMachine.getAvailableDrinks(); + assertEquals(emptyList(), availableSKUs); + } + + @Before + public void configureSplit(Scenario scenario) { + CucumberSplit.configureSplit(splitClient, scenario); + } +} diff --git a/testing/src/test/resources/cucumber.properties b/testing/src/test/resources/cucumber.properties new file mode 100644 index 000000000..b48dd63bf --- /dev/null +++ b/testing/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true diff --git a/testing/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature b/testing/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature new file mode 100644 index 000000000..bb2c98a15 --- /dev/null +++ b/testing/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature @@ -0,0 +1,25 @@ +# This tag is inherited by all the scenarios, setting the "cappuccino" split feature to "off" by default. +@split[cappuccino:off] +Feature: Make Coffee + The scenarios in this feature file describes how the coffee machine works. + + Scenario: Empty machine + Given the machine is empty + Then no drinks should be available + + Scenario: Display available drinks + Given the machine is not empty + Then the following drinks should be available: + | name | price | + | filter coffee | 0.80 | + + # The tags on this scenario will be ["@split[cappuccino:off]", "@split[cappuccino:on]"] + # The @split tags are processed sequentially, so the cappuccino split feature will be set to "off" + # and then immediately overwritten to "on". + @split[cappuccino:on] + Scenario: Display available drinks (including the new experimental cappuccino) + Given the machine is not empty + Then the following drinks should be available: + | name | price | + | filter coffee | 0.80 | + | cappuccino | 1.10 | From 06d8298b92d5b07ac793c433f021c4beb1a6e4a6 Mon Sep 17 00:00:00 2001 From: Patricio Echague Date: Mon, 26 Jul 2021 15:06:41 -0700 Subject: [PATCH 2/3] Create a separate module for cucumber --- testing-cucumber/CHANGES.txt | 5 +++ testing-cucumber/pom.xml | 38 +++++++++++++++++++ .../testing/cucumber/CucumberSplit.java | 0 .../testing/cucumber/CoffeeMachine.java | 0 .../testing/cucumber/RunCucumberTest.java | 0 .../io/split/client/testing/cucumber/SKU.java | 0 .../testing/cucumber/StepDefinitions.java | 0 .../src/test/resources/cucumber.properties | 0 .../testing/cucumber/MakeCoffee.feature | 0 testing/pom.xml | 10 ----- 10 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 testing-cucumber/CHANGES.txt create mode 100644 testing-cucumber/pom.xml rename {testing => testing-cucumber}/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java (100%) rename {testing => testing-cucumber}/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java (100%) rename {testing => testing-cucumber}/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java (100%) rename {testing => testing-cucumber}/src/test/java/io/split/client/testing/cucumber/SKU.java (100%) rename {testing => testing-cucumber}/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java (100%) rename {testing => testing-cucumber}/src/test/resources/cucumber.properties (100%) rename {testing => testing-cucumber}/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature (100%) diff --git a/testing-cucumber/CHANGES.txt b/testing-cucumber/CHANGES.txt new file mode 100644 index 000000000..8bffaed34 --- /dev/null +++ b/testing-cucumber/CHANGES.txt @@ -0,0 +1,5 @@ +CHANGES + +1.0.0(July 27, 2021) +- First release of Cucumber integration for Split testing module + diff --git a/testing-cucumber/pom.xml b/testing-cucumber/pom.xml new file mode 100644 index 000000000..303b6b478 --- /dev/null +++ b/testing-cucumber/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + io.split.client + java-client-parent + 4.2.1 + + + java-client-testing-cucumber + 1.0.0-rc1 + jar + Split Java Client Cucumber testing module + Cucumber integration for the testing module of Split Java Client + + + io.split.client + java-client-testing + ${parent.version} + + + junit + junit + + + io.cucumber + cucumber-java + 6.10.4 + + + io.cucumber + cucumber-junit + 6.10.4 + + + diff --git a/testing/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java b/testing-cucumber/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java similarity index 100% rename from testing/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java rename to testing-cucumber/src/main/java/io/split/client/testing/cucumber/CucumberSplit.java diff --git a/testing/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java b/testing-cucumber/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java similarity index 100% rename from testing/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java rename to testing-cucumber/src/test/java/io/split/client/testing/cucumber/CoffeeMachine.java diff --git a/testing/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java b/testing-cucumber/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java similarity index 100% rename from testing/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java rename to testing-cucumber/src/test/java/io/split/client/testing/cucumber/RunCucumberTest.java diff --git a/testing/src/test/java/io/split/client/testing/cucumber/SKU.java b/testing-cucumber/src/test/java/io/split/client/testing/cucumber/SKU.java similarity index 100% rename from testing/src/test/java/io/split/client/testing/cucumber/SKU.java rename to testing-cucumber/src/test/java/io/split/client/testing/cucumber/SKU.java diff --git a/testing/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java b/testing-cucumber/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java similarity index 100% rename from testing/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java rename to testing-cucumber/src/test/java/io/split/client/testing/cucumber/StepDefinitions.java diff --git a/testing/src/test/resources/cucumber.properties b/testing-cucumber/src/test/resources/cucumber.properties similarity index 100% rename from testing/src/test/resources/cucumber.properties rename to testing-cucumber/src/test/resources/cucumber.properties diff --git a/testing/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature b/testing-cucumber/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature similarity index 100% rename from testing/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature rename to testing-cucumber/src/test/resources/io/split/client/testing/cucumber/MakeCoffee.feature diff --git a/testing/pom.xml b/testing/pom.xml index d690ce04f..bbad31d7f 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -23,15 +23,5 @@ junit junit - - io.cucumber - cucumber-java - 6.10.4 - - - io.cucumber - cucumber-junit - 6.10.4 - From 5572c03efccd95cadea392548e8a39ecb77b9604 Mon Sep 17 00:00:00 2001 From: Patricio Echague Date: Mon, 26 Jul 2021 17:40:28 -0700 Subject: [PATCH 3/3] Address feedback --- pom.xml | 3 ++- testing-cucumber/pom.xml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ee6d16156..c0edf7cf6 100644 --- a/pom.xml +++ b/pom.xml @@ -70,8 +70,9 @@ 1.8 - testing client + testing + testing-cucumber diff --git a/testing-cucumber/pom.xml b/testing-cucumber/pom.xml index 303b6b478..e649a3bed 100644 --- a/testing-cucumber/pom.xml +++ b/testing-cucumber/pom.xml @@ -28,11 +28,13 @@ io.cucumber cucumber-java 6.10.4 + provided io.cucumber cucumber-junit 6.10.4 + provided