-
Notifications
You must be signed in to change notification settings - Fork 19
Add Support for Cucumber using @split[feature:treatment] tags #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
/** | ||
* <p> | ||
* Simple Cucumber plugin for Split. | ||
* </p> | ||
* <p> | ||
* Cucumber scenarios annotated with {@code @split[feature:treatment]} tags can be used to | ||
* configure a {@link SplitClientForTest} instance. | ||
* </p> | ||
* <p> | ||
* To use it, define a <a href="https://cucumber.io/docs/cucumber/api/#hooks">Before Hook</a> that invokes the {@link CucumberSplit#configureSplit(SplitClientForTest, Scenario)} | ||
* method. Example: | ||
* </p> | ||
* | ||
* <pre> | ||
* 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); | ||
* } | ||
* } | ||
* </pre> | ||
*/ | ||
public class CucumberSplit { | ||
private static final Pattern SPLIT_TAG_PATTERN = Pattern.compile("^@split\\[(.*):(.*)]"); | ||
|
||
public static void configureSplit(SplitClientForTest splitClient, Scenario scenario) { | ||
Collection<String> tags = scenario.getSourceTagNames(); | ||
for (String tag : tags) { | ||
Matcher matcher = SPLIT_TAG_PATTERN.matcher(tag); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. This is intentional. Tags that are placed at the top level We're taking advantage of this here to allow users to define "default" treatments at the feature level, and (optionally) override them at the scenario level. I have illustrated this in the last scenario where the default There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've pushed some more changes to (hopefully) illustrate better how this works. |
||
if (matcher.matches()) { | ||
String feature = matcher.group(1); | ||
String treatment = matcher.group(2); | ||
splitClient.registerTreatment(feature, treatment); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package io.split.client.testing.cucumber; | ||
|
||
import io.cucumber.junit.Cucumber; | ||
import org.junit.runner.RunWith; | ||
|
||
@RunWith(Cucumber.class) | ||
public class RunCucumberTest { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.split.client.testing.cucumber; | ||
|
||
import io.cucumber.java.Before; | ||
import io.cucumber.java.Scenario; | ||
import io.cucumber.java.en.Then; | ||
import io.split.client.testing.SplitClientForTest; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
public class StepDefinitions { | ||
private final SplitClientForTest splitClient = new SplitClientForTest(); | ||
|
||
@Then("split {string} should be {string}") | ||
public void split_should_be(String split, String expectedValue) { | ||
assertEquals(expectedValue, splitClient.getTreatment("arbitraryKey", split)); | ||
} | ||
|
||
@Before | ||
public void configureSplit(Scenario scenario) { | ||
CucumberSplit.configureSplit(splitClient, scenario); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
cucumber.publish.quiet=true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
@split[cappuccino:off] | ||
@split[dollars:on] | ||
Feature: Split | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you mind educating me how the unit test binds to the split listed in this file? also, is the idea to have a single descritptor file or one per test class or test case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you mean by unit test or descriptor file. I'll clarify some of the terminology used by Cucumber (which is closer to an acceptance testing tool than a unit testing tool). A
Cucumber creates a new instance of every class with step definitions before the scenario runs. This is similar to how JUnit creates a new instance of a test class for every For each step in the scenario, Cucumber finds a matching step definition (based on the expression on the annotation) and invokes the method. Step definition classes aren't scoped to a particular Scenarios can have tags. The code in this library looks for tags of the format |
||
This is an example of how to use Split with Cucumber | ||
|
||
@split[cappuccino:on] | ||
@split[dollars:off] | ||
Scenario: with cappuccino, but without dollars | ||
Then split "cappuccino" should be "on" | ||
And split "dollars" should be "off" | ||
|
||
Scenario: without cappuccino, but with dollars | ||
Then split "cappuccino" should be "off" | ||
And split "dollars" should be "on" | ||
|
||
@split[dollars:off] | ||
Scenario: without cappuccino, nor dollars | ||
Then split "cappuccino" should be "off" | ||
And split "dollars" should be "off" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be test scope, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've thought a bit more about this. I think there are two options here -
compile
scope orprovided
scope. (See maven docs for details).The
junit
dependency is alreadycompile
scope, so using the same scope forcucumber-java
andcucumber-junit
would be consistent with that.Alternatively, making it
provided
scope would indicate that dependent modules would have to explicitly add the dependencies themselves. I think that's ok too, if you don't want consumers of the library transitively download cucumber unless they opt into it with an explicit dependency in their own pom/gradle file.I'll defer to you to decide what's most appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If someone is already using this framework (our Java-testing) without cucumber and I merge this PR. Regardless of the provided or not, I believe the build will fail for not finding the dependency in the classpath, right?
I need time to sync internally but if that's the case we may need to split this into a separate submodule.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it won't.
In Java, classes are only loaded when they are referenced by another class. Classes are not loaded by mere presence in a jar file.
No other classes in this library reference
io.split.client.testing.cucumber.CucumberSplit
, so it won't be loaded.