Spock: Reusable Data Tables
Asked Answered
D

3

11

Can I take a test like this and extract the where clause data table into a reusable block?

@Unroll
void "test that doSomething with #a and #b does not fail"(String a, String b) {
    when:
        doSomethingWithAandB(a, b)
    then:
        notThrown(Exception)
    where:
        a     | b
        "foo" | "bar"
        "foo" | "baz"
        "foo" | "foo"
}

something like this (pseudo code):

@Unroll
void "test that doSomethingElse with #a and #b does not fail"(String a, String b) {
    when:
        doSomethingElseWithAandB(a, b)
    then:
        notThrown(Exception)
    where:
        dataTable()
}

def dataTable(a, b) {  // this is now reusable in multiple tests
        a     | b
        "foo" | "bar"
        "foo" | "baz"
        "foo" | "foo"        
}
Dentate answered 10/10, 2017 at 13:3 Comment(1)
Have a look here: #26157044Prescriptive
L
7

Table-formatted data in where clause is actually parsed as a set of OR expressions at compile time, collected into list of lists and then transposed, so this:

where:
a | b | c
1 | 0 | 1
2 | 2 | 2
4 | 5 | 5

will be transformed into this:

where:
a << [1, 2, 4]
b << [0, 2, 5]
c << [1, 2, 5]

before the test methods are generated (see org.spockframework.compiler.WhereBlockRewriter for details). This var << list construct is referred to as "data pipe" in documentation.

Upgrading the existing answers a bit, as of Spock 1.1, one can shorten the code a bit using a construct called "Multi-Variable Data Pipes", which will transpose the table:

class SampleTest extends Specification {
    @Unroll
    def "max of #a and #b gives #c"() {
        expect:
        Math.max(a, b) == c
        where:
        [a, b, c] << dataTable()
    }

    static def dataTable() {
        [
                [1, 0, 1],
                [2, 2, 2],
                [4, 5, 5]
        ]
    }
}

Fun fact: while the docs on Syntactic Variations don't explain why, it is because the table rows are parsed as a set of OR expressions, the double bars can also be used -

where:
a | b || c
1 | 0 || 1
2 | 2 || 2
4 | 5 || 5
Leonoraleonore answered 4/2, 2018 at 16:45 Comment(2)
I copied this code exactly into my spock framework and tried to run it and I got the exception: org.junit.platform.commons.JUnitException: TestEngine with ID 'spock' failed to discover tests Caused by: org.junit.platform.commons.JUnitException: MethodSelector [className = 'specs.savefilters.SaveFiltersPostSpec', methodName = 'max of #a and #b gives #c', methodParameterTypes = '']Prorogue
this actually works and it's well documented spockframework.org/spock/docs/1.0/data_driven_testing.htmlBoelter
B
5

Yes, you can.

import spock.lang.Specification
import spock.lang.Unroll

class SampleTest extends Specification {
  @Unroll
  def "max of #a and #b gives #c"() {
  expect:
    Math.max(a, b) == c
  where:
    a << aProvider()
    b << bProvider()
    c << cProvider()
 }

 private List<Integer> aProvider() {
   [1 ,2 ,4]
 }
 private List<Integer> bProvider() {
   [0 ,2 ,5]
 }

 private List<Integer> cProvider() {
    [1 ,2 ,5]
 }
}

Of course, aProvider/bProvider/cProvider can be re-written in a 'groovier way' and among other things, can be externalized to some class and reused in many tests. You don't have to specify a table, but can supply 'data pipes'. Read more in Data Driven Testing chapter.

Brackish answered 15/10, 2017 at 12:20 Comment(0)
D
0

Thanks, Mark, for your answer! Based on what I learned, I've arrived at a reasonably simple solution.

import spock.lang.Specification
import spock.lang.Unroll

class SampleTest extends Specification {
    @Unroll
    def "max of #a and #b gives #c"() {
        expect:
            Math.max(a, b) == c
        where:
            params << dataTable()
            a = params.a
            b = params.b
            c = params.c
    }

    // this is now reusable in multiple tests
    def dataTable() {
        return [
            // case 1
            [
                a: 1,
                b: 0,
                c: 1
            ],

            // case 2
            [
                a: 1,
                b: 2,
                c: 2
            ],

            // case 3
            [
                a: 4,
                b: 5,
                c: 5
            ]
        ]
    }
}
Dentate answered 26/10, 2017 at 0:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.