-
Notifications
You must be signed in to change notification settings - Fork 68
Provide an extension point to allow filtering/skipping specification examples #267
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
Conversation
This seems like a sensible PR - there's clearly a use case for it. I'd prefer for the feature to be named IGNORED instead of SKIPPED because that matches the jUnit @ignore annotation better. Now to read the code! |
|
||
@java.lang.annotation.Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Skipped { |
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 we use the jUnit @ignore annotation then we won't need this interface. Need to verify that this annotation exists in the most recent version of jUnit tho - no point relying on a dependency that we know will change!
The initial intent was to use JUnit's @ignore annotation, however it would require being consistent with JUnit's behaviour when ignoring a linked specification (which has some inconsistencies with current Unimplemented and ExpectedToFail).
|
* @param filter the filter | ||
* @return this | ||
*/ | ||
ConcordionExtender withExampleFilter(ExampleFilter filter); |
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 a new method in one of our API interfaces - so will need a version bump for Concordion to let users of this API adjust their code.
@Override | ||
public ResultSummary getMeaningfulResultSummary(ResultSummary rs, FailFastException ffe) { | ||
assertIsSatisfied(rs, ffe); | ||
return new SingleResultSummary(Result.IGNORED); |
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 here we have skipped being processed as ignored for the results. So another indicator they are probably the same concept. I've read your comment about the conceptual mismatch with jUnit - it's a good point and I'll have a think about it (woke up early today and brain not yet working!)
@@ -39,7 +40,11 @@ public void execute(CommandCall node, Evaluator evaluator, ResultRecorder result | |||
} | |||
|
|||
try { | |||
node.getChildren().processSequentially(evaluator, resultRecorder); | |||
if (exampleFilter != null && exampleFilter.shouldSkip(node.getElement())) { |
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 nice - one day we'll use a version of Java with Lambdas and convert the exampleFilter to one of the standard interfaces.
* @param exampleElement the element to evaluate | ||
* @return <tt>true</tt> if example should be skipped, <tt>false</tt> otherwise | ||
*/ | ||
boolean shouldSkip(Element exampleElement); |
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 just realised - there is potential for this in future to also mark tests as unimplemented, expected to fail, and expected to pass in future.
Have now finished reviewing the PR. I like the idea - There is an interesting use case for disabling a different set of tests at runtime - so that a user can run different concordion tests locally, in PR builds, or in deploys - in case some tests are slow or unreliable in a particular build pipeline step. We do that at work right now (but sadly don't use concordion at my current job!) Here are my more specific thoughts:
|
I've started a convo on the concordion-dev mailing list to chat about those thoughts. It'd be great if you joined so that we can chat about your PR together. Naturally we're super happy to have your PR and don't want to force you to do lots of work to get it merged! |
Hopefully this link works: https://groups.google.com/forum/#!forum/concordion-dev |
<ul> | ||
<li>Success: <span concordion:assert-equals="processingResult.successCount">1</span></li> | ||
<li>Failed: <span concordion:assert-equals="processingResult.failureCount">1</span></li> | ||
<li>Exceptions: <span concordion:assert-equals="processingResult.exceptionCount">0</span></li> |
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.
Can we also check ignored/skipped example counts?
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.
Will do.
The main use case and motivation was to allow concordion to hook into spring's profile mechanism where examples can be tagged with a param (e.g. spring-profile='smoke-test, all') and conditionally run them based on the active profile or similar purpose of @IfProfileValue. The semantic difference of Unimplemented/ExpectedToFail vs Ignored/Skipped is that the former refers to the state of the example while the latter refers to the result of the run. An example could be declared as Unimplemented/ExpectedToFail and still be ignored at runtime. The main difference is concordion allows Unimplemented/ExpectedToFail to execute while the latter would not. Considering this, IGNORED doesn't belong to the ImplementationStatus - the only reason it's there is for the additional notes in the logs and html. I agree to use JUnit's @ignore, so it would be consistent with the behavior of an @ignore annotated fixture (though there's actually no way of annotating examples). However, I disagree that Unimplemented/ExpectedToFail should be override-able/set-able at runtime as these are the state of tests - is there a use case for them to change in between runs given the same version of test and system being tested? Also, setupCommandForExample is called after the example has already ran (seems the code inside is meant to update the example element based on the ImplementationStatus). Lastly, this is not a breaking change as it doesn't remove or modify any existing API so a minor version bump (signifying a new feature) would be appropriate. Thanks for the invite, will be happy to discuss further in the forum later. |
Committed all requested changes - for your review. Thanks! |
I hope no one minds me poking my head in here, but I saw the discussion about using JUnit's @ignore annotation. I would be hesitant to tie functionality to it because JUnit 5 replaced it.
IMO, it seems like it would be better for concordion to define its own API in this regard. Edited for clarity |
I agree @miralexan (and thanks for poking your head in). We should remove this reliance on JUnit. Ideally, only the I'd suggest we make this a Concordion specific flag. If it will only apply to examples, and not specifications, can we use |
@miralexan I agree that's why the initial changes doesn't depend on JUnit lib. @nigelcharman will introduce the To add, this PR is targeted for Concordion 2.x only because 3.0 (being targeted for JUnit 5) won't need it - thus should be deprecated in that release. |
@chiknrice I don't think that JUnit 5 provides this functionality. The This extension allows individual examples to be ignored. Why do you think JUnit 5 would deprecate it? |
* @param exampleElement the element to evaluate | ||
* @return the status based on the exampleElement | ||
*/ | ||
ImplementationStatus getStatusForExample(Element exampleElement); |
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'm a little wary of using the mutable Element as a parameter here. I now we have got some exposure of the DOM to extensions, I'd rather reduce the coupling. What do you think we need from the Element here? Just example name, or attributes or also children?
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.
Not sure what we should expose in place of Element, do you mean change the parameter to be limited to name and attributes (perhaps as a map)?
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.
How about an ExampleDefinition
interface that has getName()
, getAttributeValue(String name)
, getAttributeValue(String localName, String namespaceURI)
(based on what is already exposed by Element
), or getAttributeMap()
(based on a new Element
method)?
Maybe also a getText()
method that returns the text of the <a>
element?
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.
will implement that - though not sure why we need getText for element? isn't example commands usually affixed to
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.
Sorry, I've been doing too much Concordion markdown lately. In Markdown it's on a link within a header. I forgot that we translate to a <div>
in HTML. Please ignore getText()
.
I've experimented with a few other cases:
|
@nigelcharman what I mean by deprecating this feature in concordion 3.0 is because skipping the examples can be achieved already by JUnit 5 not via |
Possibly, at the moment, I'm thinking that the |
@chiknrice a bit of tangent, but based on the current state of Junit 5 I think that it's unlikely that concordion would be able to leverage the If concordion 3.0 is implemented as it's own Junit platform
|
@miralexan thanks for pointing that out, wasn't aware of the concordion team's plans for 3.0 hence my continued assumption of how examples work. If that's the case then good - there's still a use for this PR in 3.0 :) |
…upling with DOM API
…or ignored examples
…o chiknrice fork
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.
Looks good to go. Are you ready for me to merge it?
yes, fork is up to date, thanks! |
OK, I've merged it. I'm finishing off #264 which has a number of breaking API changes and hope to release both at the same time - possibly a week or two away. |
I guess just "New extension point to allow programmatically setting the implementation status of examples". I don't have an example at the moment, but I plan to create an extension leveraging spring profiles - will just link to it later. Thanks! |
The changes are only related to the extension facility. The ExampleFilter api is introduced to allow a user to inject conditional logic to concordion which would skip the execution of a specification's example based on its xml Element. By default there is no ExampleFilter (no bundled filter). Injecting the filter is done via ConcordionExtender.withExampleFilter method. Matching examples would be reported with "<-- Note: This example has been marked as SKIPPED" (both in the logs and the resulting html); this was done via the already available ImplementationStatus/Checker. Skipped examples would still be announcing start and end examples to registered ExampleListeners.