Q:

Spock - repeating tests with different system defaults

I have a Spock spec for testing a method that takes a java.util.Date.
def "special dates are identified correctly"() {
    expect:
        isSpecialDay(Date.parse('yyyy/MM/dd', date)) == special
    where:
        date         | special
        '2010/01/01' | false
        '2011/01/01' | true
        '2012/01/01' | true
        '2013/01/01' | false
        '2014/01/01' | true
        // and lots more...
}

I want to ensure that TimeZone doesn't make a difference in my method implementation (i.e. Jan 1st, 2011 is special regardless if I'm in EST or GMT or whatever). Is there a way I can repeatedly execute the test method in a single run with a different, default timezone per execution?
I could add a third column to the "where" block for the TimeZone, but that extra dimension would make the table too big for my liking.
Currently, I'm setting a random default per test run, but I don't like the fact that my test is not repeatable and, if there's a failure, the problematic TimeZone isn't captured in the assertion message.
@Shared TimeZone defaultTz = TimeZone.getDefault()

def setupSpec() {
    def tzIds = TimeZone.getAvailableIDs()
    def randomTzId = tzIds[new Random().nextInt(tzIds.length)]
    def randomTz = TimeZone.getTimeZone(randomTzId)
    println "Using TimeZone $randomTz for test spec"
    TimeZone.setDefault(TimeZone.getTimeZone(randomTzId));
}

def cleanupSpec() {
    TimeZone.setDefault(defaultTz)
}

A:

Here's an example using the combinations trick I hinted at above:
@Grab(group='joda-time', module='joda-time', version='2.9')
@Grab(group='org.spockframework', module='spock-core', version='1.0-groovy-2.4')

import spock.lang.*
import org.joda.time.DateTime
import org.joda.time.DateTimeZone

class TestSpecialDate extends Specification {
    @Shared def zoneCombinations = [
        DateTimeZone.availableIDs,
        [[date:'2010/01/07', special:false], [date:'2011/01/01', special:true], [date:'2012/01/01', special:true],
         [date:'2013/11/06', special:false], [date:'2014/01/01', special:true]]]
             .combinations { a, b -> [zone:a, date:b.date, special:b.special] }

    @Unroll
    def "#date for #zone should be special #special"() {
        expect:
        // get current moment in default time zone
        DateTime dt = new DateTime( Date.parse( 'yyyy/MM/dd', date ) )

        // translate to local date time
        DateTime dtLocal = dt.withZone( DateTimeZone.forID( zone ) )

        // Get Java Date and assert  
        isSpecialDay( dtLocal.toDate() ) == special

        where:
        date << zoneCombinations.date
        special << zoneCombinations.special
        zone << zoneCombinations.zone
    }

    // Mimic special day implementation
    static private boolean isSpecialDay(Date date) {
        // Check if it is the first day of month
        return date[Calendar.DAY_OF_MONTH] == 1
    }
}

When executed in the groovy console, that runs:
JUnit 4 Runner, Tests: 2915, Failures: 0, Time: 351

2915 tests :-)

A:

Using JodaTime you can test the same for all available time zones usingDateTimeZone.getAvailableIDs(). Here is a quick and nasty implementation to show case how it cane be done.
@Grab(group='joda-time', module='joda-time', version='2.9')
@Grab(group='org.spockframework', module='spock-core', version='1.0-groovy-2.4')

import spock.lang.*
import org.joda.time.DateTime
import org.joda.time.DateTimeZone

class TestSpecialDate extends Specification {

    def "special dates are identified correctly"() {
        expect:
        DateTimeZone.availableIDs.each { tz ->
            // get current moment in default time zone
            DateTime dt = new DateTime( Date.parse( 'yyyy/MM/dd', date ) )

            // translate to local date time
            DateTime dtLocal = dt.withZone( DateTimeZone.forID( tz ) )

            // Get Java Date and assert  
            assert isSpecialDay( dtLocal.toDate() ) == special
        }

        where:
        date         || special
        '2010/01/07' || false
        '2011/01/01' || true
        '2012/01/01' || true
        '2013/11/06' || false
        '2014/01/01' || true
    }

    // Mimic special day implementation
    static private boolean isSpecialDay(Date date) {
        // Check if it is the first day of month
        return date[Calendar.DAY_OF_MONTH] == 1
    }
}

