-
Notifications
You must be signed in to change notification settings - Fork 23
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
Adding Gherkin-like feature set, see #50 #51
Conversation
*/ | ||
@Deprecated |
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.
Could we possibly even remove this type altogether?
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.
Possibly in the future. The public API was originally all under the Spectrum
type, basically all users have references to Spectrum.Block
. I had to split the interface out to a separate file to resolve some internal circular dependencies. IIRC, I was also considering adding some internal methods to Block
that would not be required/exposed on the public API, but didn't follow through with them.
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.
So, I'd keep Spectrum.Block
in place for backward/semver compatibility. Spectrum 1.0.0
is out there now, and we shouldn't remove/break any parts of its public API until 2.x
. We can deprecate it now, and that would be a semver-minor bump (1.1.x
).
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.
Then it stays - it's essentially just a rename of the public Block
now. It kinda messes up the rest of the usage of Block in Spectrum, but better that "core" Block be the way forward.
/** | ||
* For use with nested steps inside a test, this box allows data passing between tests | ||
*/ | ||
public class Box<T> { |
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.
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 could rename it Value if you like. I see it used to be hiding inside the Spectrum class and used to have public field access :(
The let()
construct is a nice idea, but is intended to stop leaking between specs. In a BDD context, the leaf nodes aren't specs, they're steps, and as such should be able to communicate. Boxed values would like us do that.
Tell me what to change to make you approve (other than the failing javadoc :))
* @param block the contents of the scenario - given/when/then steps | ||
*/ | ||
public static void scenario(final String scenarioName, final Block block) { | ||
describeChain("Scenario: " + scenarioName, block); |
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.
Now uses a causal link within the steps to stop step running when there's a failure earlier in the list.
@FunctionalInterface | ||
interface Block { | ||
public interface Block { |
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.
Made public as this should be the entry point for users in future.
* {@link #it(String, com.greghaskins.spectrum.Block) it} that define each expected behavior | ||
* | ||
*/ | ||
public static void describeChain(final String context, final com.greghaskins.spectrum.Block block) { |
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 is where the requirements of BDD differ to the describe/it syntax. A BDD test should probably stop when a step fails. This allows anyone to say that their Specs within a suite are causally linked.
@FunctionalInterface | ||
interface ChildRunner { | ||
void runChildren(final Suite suite, final RunNotifier notifier); | ||
} |
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 allows the running of children to be a strategy which can vary. A sort of micro subclassing.
@@ -13,6 +14,8 @@ static NotifyingBlock wrap(final Block block) { | |||
return (description, notifier) -> { | |||
try { | |||
block.run(); | |||
} catch (final AssumptionViolatedException assumptionViolation) { | |||
notifier.fireTestAssumptionFailed(new Failure(description, assumptionViolation)); |
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.
Technically an assumption failure should be reported differently. The coverage tool made me notice this.
}); | ||
|
||
scenarioOutline("rerun template scenario for", Stream.of("A", "B", "C"), | ||
letter -> { |
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.
Another idea stolen from Cucumber - the scenario outline works on any input object and allows the whole internal definition to be reused. This is parameterised testing done easily!
scenarioOutline("outer parameterised scenario with", Stream.of("A", "B", "C"), | ||
letter1 -> { | ||
scenarioOutline("inner parameterised scenario with", Stream.of("Z", "X", "Y"), | ||
letter2 -> { |
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.
Nesting scenario outlines with data passing in seems nice too.
b043a1d
to
525fdb6
Compare
Odd build problem:
is failing, but the code MUST all be checked in, or how would it have gotten there! Unless there's something funny in your gradle that's rewriting files. |
Instead of failing on style rules, I try to have the build fix things itself where possible (indentation, etc). This error means you have not checked in the "final" version of those files (unstaged local changes). |
As I feared, running gradle under Windows has caused the files to have their CRLFs mangled. Yuck. Any idea how to tell gradle not to be so annoying? Or do I have to do my final commits on a Linux command line...? |
Ahaha! I added a .gitattributes file to control EOL - this means that dumb collaborators like me have less chance of breaking the merges when Spotless rewrites the files. |
Getting that build to pass was extremely time consuming... We're there now! Yay. Coverage is basically 100%. Not ready for check-in yet. Any feedback gratefully received. Going to look at more stuff relating to ignoring and tagging, since my aim for this is to be able to substitute this for Cucumber in terms of BDD expressiveness and usage in a complex test suite. |
BTW style rules that fail builds because of periods in JavaDoc... :( |
I think there's a risk of dissonance between my just-submitted |
Tagging functionality now provided. This adds onto the general purpose ignore feature. It allows suites and specs to be given a series of tags and for them to be selected for running based on those tags. Example usage would be to mark a specific suite as WIP and add something to the system properties to either not run it or ONLY run it. This is a bit like the focus feature or the temp ignore feature. It's stronger because it might allow you to configure CI never to run WIP tests, or configure separate categories of tests to run in different builds. |
@greghaskins I'm basically done with the Gherkin/Cucumber type features. I have already prototyped getting JUnit rules into the code, so would like to have a crack at doing that properly. It makes sense for me to branch off from this code to do it, but that's going to be self-defeating if, in code review, you come up with some major changes. Do you think you'll be able to give this PR a quick review so I can see whether I should delay the next step? |
Sure. I'll check it out this evening. I haven't seen everything yet, but it does sound like this is quite a lot of functionality for one PR. Tagging & alternate ignore syntax sound like separate features, for example. PRs also accepted on the build script and CONTRIBUTING.md instructions. I use the included config files with my IDE, and run gradle on the command-line often in my TDD cycle. But I'm just one dev, so there are probably things that work better across people and platforms. |
@ashleyfrieze Really nice work. This is looking like a super easy-to-use way to declare Gherkin-style tests. In my experience, Cucumber-JVM has been a pain because of all the step definitions required behind the scenes. This makes tests readable for non-programmers, but simple to implement/edit/refactor for developers. Win! Some quick points/questions:
Now, a few bigger points. For starters, let’s focus this PR down on just the Gherkin-style test declarations, so we can get it merged quickly and move on to additional features. Specifically, we should break out separate PRs for:
I think the commits you made are pretty atomic, so we should be able to port these features over to separate PRs fairly easily (I can help too). I don’t want the back-and-forth around these related but separate ideas to stand in the way of getting a slick new Gherkin syntax in place. For the Gherkin implementation itself, we’ve introduced some fairly tight coupling between Suite “children” to achieve the given/when/then workflow (which is required, you can't run Come to think of it, what about going one step further? Gherkin-style tests really operate in a different way and with different rules than spec-style tests. It might be cleaner if there was a proper object model for I'm interested to hear your take on that. Wow, lots of words! Overall, this work represents a really solid upgrade for Spectrum. Please let me know how I can help further, including collaborating on the code itself. I also set up a chat room over at https://gitter.im/greghaskins/spectrum if you want an easier way to communicate directly. |
392d746
to
a58cb16
Compare
@greghaskins - wise feedback. A lot of it. I have responded with a push.
I would like to leave the I'm really aiming to get to JUnit rules, and we're getting caught up at the starting blocks. I'd like to use Spectrum with this feature set in the very near future. Let's focus just on this PR - what else should change to get this syntax in and merged? Then we can re-open the subject of ignoring/tagging. Then we can do rules. |
import com.greghaskins.spectrum.Block; | ||
|
||
import java.util.function.Consumer; | ||
import java.util.stream.Stream; |
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.
Consumer
and Stream
seem to be unused imports in this file, FWIW.
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.
Gone now.
…hen/Then/And steps to be used. The Scenarios are treated as a block and failures within the block stop the scenario. The class has been added to allow state to be shared by steps or specs. A few minor Java 8 refactorings included also.
8647e11
to
629c7fb
Compare
Nice work; merging now. I have a couple minor fixups, mostly for consistency in the JavaDocs and README which I'll submit as a follow-up PR. I realized with our latest change, the public API for the Gherkin syntax will be under the root package, in |
WIP at the moment - relates to #50