-
Notifications
You must be signed in to change notification settings - Fork 471
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
Support defining conditions in @Verify
/@VerifyAll
helper methods
#2112
base: master
Are you sure you want to change the base?
Support defining conditions in @Verify
/@VerifyAll
helper methods
#2112
Conversation
Reuse the `ConditionRewriter` to transform condition expressions in methods annotated with `spock.lang.Verify`. The method can be defined in any class, not only the ones extending from `Specification`. The method is required to have a `void` return type. Implicit condition assertions on all levels will be transformed Signed-off-by: Pavlo Shevchenko <[email protected]>
Reuse the `VerifyMethodsRewriter` to rewrite implicit conditions in helper methods annotated with `@VerifyAll`. The only difference is to use `ErrorCollector` instead of `ErrorRethrower` to not rethrow the exception immediately, but to collect all failures and throw them at the end. Signed-off-by: Pavlo Shevchenko <[email protected]>
Signed-off-by: Pavlo Shevchenko <[email protected]>
Signed-off-by: Pavlo Shevchenko <[email protected]>
Signed-off-by: Pavlo Shevchenko <[email protected]>
Signed-off-by: Pavlo Shevchenko <[email protected]>
Signed-off-by: Pavlo Shevchenko <[email protected]>
cf60234
to
1ccb09b
Compare
spock-core/src/main/java/org/spockframework/compiler/condition/BaseVerifyMethodRewriter.java
Show resolved
Hide resolved
Signed-off-by: Pavlo Shevchenko <[email protected]>
…FailedWithExceptionError` Signed-off-by: Pavlo Shevchenko <[email protected]>
Signed-off-by: Pavlo Shevchenko <[email protected]>
1ccb09b
to
ebe0044
Compare
Signed-off-by: Pavlo Shevchenko <[email protected]>
spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
Outdated
Show resolved
Hide resolved
@@ -276,7 +277,7 @@ public Store(IterationInfo iterationInfo, Path rootPath, boolean updateSnapshots | |||
this.extension = Assert.notNull(extension); | |||
this.charset = Assert.notNull(charset); | |||
|
|||
Class<?> specClass = iterationInfo.getFeature().getSpec().getReflection(); | |||
Class<?> specClass = iterationInfo.getFeature().getParent().getBottomSpec().getReflection(); |
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.
💬 Allows defining snapshot-based in base classes, but child specs can overwrite snapshots.
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 potentially breaking change, but it hasn't been in a final release yet. So it should be fine. We should document it in the release notes nonetheless.
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 added release notes in 49d2685
(#2112). I think there is value in supporting both scenarios: you may want to define features and snapshots for base class, but overwrite them for certain features.
...smoke/ast/condition/VerifyAllMethodsAstSpec/ignores_conditions_in_overwritten_methods.groovy
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/compiler/HelperMethodsVisitor.java
Outdated
Show resolved
Hide resolved
spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
Outdated
Show resolved
Hide resolved
@@ -276,7 +277,7 @@ public Store(IterationInfo iterationInfo, Path rootPath, boolean updateSnapshots | |||
this.extension = Assert.notNull(extension); | |||
this.charset = Assert.notNull(charset); | |||
|
|||
Class<?> specClass = iterationInfo.getFeature().getSpec().getReflection(); | |||
Class<?> specClass = iterationInfo.getFeature().getParent().getBottomSpec().getReflection(); |
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 potentially breaking change, but it hasn't been in a final release yet. So it should be fine. We should document it in the release notes nonetheless.
spock-specs/src/test/groovy/org/spockframework/smoke/VerifyAllMethodsSpecification.groovy
Outdated
Show resolved
Hide resolved
spock-specs/src/test/groovy/org/spockframework/smoke/VerifyMethodsSpecification.groovy
Outdated
Show resolved
Hide resolved
spock-specs/src/test/groovy/org/spockframework/smoke/VerifyMethodsSpecification.groovy
Outdated
Show resolved
Hide resolved
...smoke/ast/condition/VerifyAllMethodsAstSpec/ignores_conditions_in_overwritten_methods.groovy
Show resolved
Hide resolved
...specs/src/test/groovy/org/spockframework/smoke/ast/condition/BaseVerifyMethodsAstSpec.groovy
Show resolved
Hide resolved
…s-in-helper-methods
…s thrown by our ErrorCollector
snapshotter.assertThat(result.source).matchesSnapshot() | ||
} | ||
|
||
def "transforms only top-level conditions"() { |
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 wonder if we want to keep that behavior for these annotations. It is a classic foot gun. If it wouldn't break anything I would probably do away with that restriction in the Spec itself. However, that would have to wait until Spock 3.
Inside with
or verifyAll
we already support assertions in if
and so on.
Thoughts: @Vampire, @AndreasTu ?
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 would not keep this behavior for the new feature, if it is simple to change it for the new one and preserve the old behavior in specs until Spock 3.
processVerificationHelperMethod((MethodNode) node, errorReporter, sourceLookup); | ||
} | ||
} finally { | ||
sourceLookup.close(); |
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.
🤔 should we make SourceLookup
AutoClosable
so we could use it in try-with?
|
||
then: | ||
result.failures.size() == 1 | ||
with(result.failures[0].exception, MultipleFailuresError) { |
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.
Isn't it
with(result.failures[0].exception, MultipleFailuresError) { | |
with(result.failures[0].exception, SpockMultipleFailuresError) { |
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.
Thanks a lot for your work! 👍
I like the idea for these annotations.
* may contain conditions, allowing to omit the {@code assert} keyword. | ||
* As in expect-blocks and then-blocks, variable declarations | ||
* and void method invocations will not be considered conditions. | ||
* The method can be defined in the {@link Specification} class or in a separate class. |
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.
Maybe add that it needs to be a Groovy class.
* The method can be defined in the {@link Specification} class or in a separate class. | |
* The method can be defined in the {@link Specification} class or in a separate Groovy class. |
* and void method invocations will not be considered conditions. | ||
* The method can be defined in the {@link Specification} class or in a separate class. | ||
* The method must have a {@code void} return type. | ||
* The test will fail on the first failing assertion. |
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.
* The test will fail on the first failing assertion. | |
* A test calling the method will fail on the first failing assertion. |
* may contain conditions, allowing to omit the {@code assert} keyword. | ||
* As in expect-blocks and then-blocks, variable declarations | ||
* and void method invocations will not be considered conditions. | ||
* The method can be defined inside the {@link Specification} class or in a separate class. |
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.
* The method can be defined inside the {@link Specification} class or in a separate class. | |
* The method can be defined inside the {@link Specification} class or in a separate Groovy class. |
snapshotter.assertThat(result.source).matchesSnapshot() | ||
} | ||
|
||
def "transforms only top-level conditions"() { |
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 would not keep this behavior for the new feature, if it is simple to change it for the new one and preserve the old behavior in specs until Spock 3.
@@ -23,7 +21,7 @@ public void validateCollectedErrors() throws Throwable { | |||
throw throwables.get(0); | |||
|
|||
default: | |||
throw new MultipleFailuresError("", throwables); | |||
throw new SpockMultipleFailuresError("", throwables); |
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.
Maybe document in the release_notes that now the subclass SpockMultipleFailuresError
is thrown instead of MultipleFailuresError
.
Sometimes people do strange checks, and this would at least docoment the change, although it is a compatible one.
this.nodeCache = nodeCache; | ||
this.lookup = lookup; | ||
this.errorReporter = errorReporter; | ||
this.errorRecorders = errorRecorders; |
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.
Please add Objects.requireNonNull()
to indicate non-null params or add @Nulable
annotations.
this.nodeCache = nodeCache; | |
this.lookup = lookup; | |
this.errorReporter = errorReporter; | |
this.errorRecorders = errorRecorders; | |
this.nodeCache = requireNonNull(nodeCache); | |
this.lookup = requireNonNull(lookup); | |
this.errorReporter = requireNonNull(errorReporter); | |
this.errorRecorders = requireNonNull(errorRecorders); |
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.
For internal code it should be org.spockframework.util.Assert#notNull(T)
and for user facing code it would be org.spockframework.util.Checks#notNull
.
private final AstNodeCache nodeCache; | ||
|
||
public DefaultConditionErrorRecorders(AstNodeCache nodeCache) { | ||
this.nodeCache = nodeCache; |
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.nodeCache = nodeCache; | |
this.nodeCache = requireNonNull(nodeCache); |
this.resources = resources; | ||
this.methodNode = methodNode; |
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.resources = resources; | |
this.methodNode = methodNode; | |
this.resources = requireNonNull(resources); | |
this.methodNode = requireNonNull(methodNode); |
Description
The PR adds two new annotations:
@spock.lang.Verify
and@spock.lang.VerifyAll
that can be applied to helper methods containing condition blocks. Top-level implicit or explicit assertion statements in these methods will be rewritten using theConditionRewriter
.@Verify
and@VerifyAll
helpers can be defined in any class, not only in the ones extending fromSpecification
. The method is required to have avoid
return type.