Skip to content

Commit

Permalink
Make @PendingFeature repeatable (#1030)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vampire committed Jul 13, 2020
1 parent 3370650 commit 1290fa7
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 3 deletions.
14 changes: 11 additions & 3 deletions docs/extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,18 @@ Groovy has the `groovy.transform.NotYetImplemented` annotation which is similar
* if at least one iteration of a data-driven test fails it will be reported as skipped
* if every iteration of a data-driven test passes it will be reported as error

[source,groovy]
[source,groovy,indent=0]
----
include::{sourcedir}/extension/PendingFeatureDocSpec.groovy[tag=example-a]
----

It is also supported to have multiple `@PendingFeature` annotations or a mixture of `@PendingFeature` and
`@PendingFeatureIf`, for example to ignore certain exceptions only under certain conditions or show different
reasons for different exceptions.

[source,groovy,indent=0]
----
@PendingFeature
def "not implemented yet"() { ... }
include::{sourcedir}/extension/PendingFeatureDocSpec.groovy[tag=example-b]
----

=== PendingFeatureIf
Expand Down
2 changes: 2 additions & 0 deletions docs/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ include::include.adoc[]

- `@ConfineMetaClassChanges` is now repeatable

- `@PendingFeature` is now repeatable


== 2.0-M3 (2020-06-11)

Expand Down
11 changes: 11 additions & 0 deletions spock-core/src/main/java/spock/lang/PendingFeature.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@ExtensionAnnotation(PendingFeatureExtension.class)
@Repeatable(PendingFeature.Container.class)
public @interface PendingFeature {
/**
* Configures which types of Exceptions are expected in the pending feature.
Expand All @@ -51,4 +52,14 @@
* @return reason why this feature is pending
*/
String reason() default "";

/**
* @since 2.0
*/
@Beta
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@interface Container {
PendingFeature[] value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.spockframework.docs.extension

import org.spockframework.runtime.extension.builtin.PreconditionContext
import spock.lang.PendingFeature
import spock.lang.PendingFeatureIf
import spock.lang.Specification

class PendingFeatureDocSpec extends Specification {
// tag::example-a[]
@PendingFeature
def "not implemented yet"() {
// end::example-a[]
expect:
false
}

// tag::example-b[]
@PendingFeature(
exceptions = UnsupportedOperationException,
reason = 'operation not yet supported')
@PendingFeature(
exceptions = IllegalArgumentException,
reason = 'arguments are broken')
@PendingFeatureIf(
value = { os.windows },
reason = 'Does not yet work on Windows')
def "I have various problems in certain situations"() {
// end::example-b[]
expect:
throw new IllegalArgumentException()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ def bar() {
}
}

def "@PendingFeature marks failing feature as skipped even if applied twice"() {
when:
def result = runner.runSpecBody """
@PendingFeature
@PendingFeature
def bar() {
expect: false
}
"""

then:
verifyAll(result) {
testsStartedCount == 1
testsSucceededCount == 0
testsFailedCount == 0
testsSkippedCount == 0
testsAbortedCount == 1
testEvents().aborted().assertEventsMatchExactly(
abortedWithReason(message('Feature not yet implemented correctly.'))
)
}
}

def "@PendingFeature includes reason in exception message"() {
when:
def result = runner.runSpecBody """
Expand All @@ -61,6 +84,52 @@ def bar() {
}
}

def "@PendingFeature includes last reason in exception message if applied twice"() {
when:
def result = runner.runSpecBody """
@PendingFeature(reason='42')
@PendingFeature(reason='4711')
def bar() {
expect: false
}
"""

then:
verifyAll(result) {
testsStartedCount == 1
testsSucceededCount == 0
testsFailedCount == 0
testsSkippedCount == 0
testsAbortedCount == 1
testEvents().aborted().assertEventsMatchExactly(
abortedWithReason(message('Feature not yet implemented correctly. Reason: 4711'))
)
}
}

def "@PendingFeature includes reason of matching annotation in exception message if applied twice"() {
when:
def result = runner.runSpecBody """
@PendingFeature(exceptions=IllegalArgumentException, reason='42')
@PendingFeature(exceptions=IllegalAccessException, reason='4711')
def bar() {
expect: throw new IllegalArgumentException()
}
"""

then:
verifyAll(result) {
testsStartedCount == 1
testsSucceededCount == 0
testsFailedCount == 0
testsSkippedCount == 0
testsAbortedCount == 1
testEvents().aborted().assertEventsMatchExactly(
abortedWithReason(message('Feature not yet implemented correctly. Reason: 42'))
)
}
}

def "@PendingFeature marks feature that fails with exception as skipped"() {
when:
def result = runner.runSpecBody """
Expand Down Expand Up @@ -107,6 +176,30 @@ def bar() {
}
}

def "@PendingFeature rethrows non handled exceptions even if applied twice"() {
when:
def result = runner.runSpecBody """
@PendingFeature(exceptions=IndexOutOfBoundsException)
@PendingFeature(exceptions=IllegalAccessException)
def bar() {
expect:
throw new IllegalArgumentException()
}
"""

then:
verifyAll(result) {
testsStartedCount == 1
testsSucceededCount == 0
testsFailedCount == 1
testsSkippedCount == 0
testsAbortedCount == 0
testEvents().failed().assertEventsMatchExactly(
finishedWithFailure(instanceOf(IllegalArgumentException))
)
}
}

def "@PendingFeature marks passing feature as failed"() {
when:
def result = runner.runSpecBody """
Expand All @@ -129,6 +222,29 @@ def bar() {
}
}

def "@PendingFeature marks passing feature as failed even if applied twice"() {
when:
def result = runner.runSpecBody """
@PendingFeature
@PendingFeature
def bar() {
expect: true
}
"""

then:
verifyAll(result) {
testsStartedCount == 1
testsSucceededCount == 0
testsFailedCount == 1
testsSkippedCount == 0
testsAbortedCount == 0
testEvents().failed().assertEventsMatchExactly(
finishedWithFailure(message('Feature is marked with @PendingFeature but passes unexpectedly'))
)
}
}

def "@PendingFeature ignores aborted feature"() {
when:
def result = runner.runSpecBody """
Expand Down

0 comments on commit 1290fa7

Please sign in to comment.