Spock - 使用不同的系统默认值重复测试

Spock - repeating tests with different system defaults

我有一个 Spock 规范,用于测试采用 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...
}

我想确保 TimeZone 不会对我的方法实现产生影响(即 2011 年 1 月 1 日是特殊的,无论我处于 EST 还是 GMT 或其他什么)。 有没有一种方法可以在单个 运行 中重复执行测试方法,每次执行时使用不同的默认时区?

我可以将第三列添加到时区的 "where" 块,但额外的维度会使 table 对我来说太大了。

目前,我正在为每个测试设置一个随机默认值 运行,但我不喜欢我的测试不是 repeatable 的事实,并且如果失败,有问题的断言消息中未捕获时区。

@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)
}

使用 JodaTime 您可以使用 DateTimeZone.getAvailableIDs() 测试所有可用时区的相同内容。这是一个快速而令人讨厌的实现,以展示如何完成它。

@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
    }
}

这是一个使用我在上面暗示的组合技巧的例子:

@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
    }
}

在 groovy 控制台中执行时,运行:

JUnit 4 Runner, Tests: 2915, Failures: 0, Time: 351

2915 次测试:-)