Search code examples
javaspringspockjava-time

Test time in Java. Change spock stub value


I have defined bean in my spring application.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Clock;

@Configuration
public class ClockConfiguration {

    @Bean
    Clock getSystemDefaultZoneClock() {
        return Clock.systemDefaultZone();
    }
}

Then in my test I want to Stub this bean.

import io.github.jhipster.config.JHipsterConstants
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.test.context.ActiveProfiles
import spock.lang.Specification
import spock.mock.DetachedMockFactory

import java.time.Clock
import java.time.Instant
import java.time.LocalDateTime

import static java.time.ZoneId.systemDefault

@ActiveProfiles(profiles = JHipsterConstants.SPRING_PROFILE_TEST)
@EnableConfigurationProperties
@SpringBootTest(classes = [MyApp])
class ClockTest extends Specification {


    @Autowired
    Clock clock

    //2018-04-01 at 10:00am
    private static Instant REFERENCE_DATE_TIME = LocalDateTime.of(2018, 4, 1, 10, 0)
        .atZone(systemDefault())
        .toInstant()

    //2018-04-01 at 10:00am
    private static Instant OTHER_REFERENCE_DATE_TIME = LocalDateTime.of(2018, 4, 2, 10, 0)
        .atZone(systemDefault())
        .toInstant()

    def "should return different date"() {
        when:
        clock.instant() >> REFERENCE_DATE_TIME
        then:
        clock.instant() == REFERENCE_DATE_TIME
        when:
        clock.instant() >> OTHER_REFERENCE_DATE_TIME
        then:
        clock.instant() == OTHER_REFERENCE_DATE_TIME

    }


    @TestConfiguration
    static class Mocks {
        def detachedMockFactory = new DetachedMockFactory()

        @Bean
        Clock clock() {
            return detachedMockFactory.Stub(Clock)
        }
    }

}

This test fails, because of second assertions. My stubed bean returns value declared by first interactions.

  1. I would like to understand why this interaction is not changed.
  2. I am looking for ideas how to redesign application. My goal is to

    when:

    //stub Clock with some value.

    //Do some logic. Use clock multiple times.

    //Change Clock to another instance.

    //Do some other logic. Use clock multiple times.

    then:

    //Make some assertion. (for example check time difference)


Solution

  • I come up with solution where I am using feature where I can compute return value.

    http://spockframework.org/spock/docs/1.2/all_in_one.html#_computing_return_values

    and in there I replace returned value.

    import spock.lang.Specification
    
    import java.time.Clock
    import java.time.Instant
    import java.time.LocalDateTime
    
    import static java.time.ZoneId.systemDefault
    
    class ClockStubTest extends Specification {
        private static Instant REFERENCE_DATE_TIME = LocalDateTime.of(2018, 4, 1, 10, 0)
            .atZone(systemDefault())
            .toInstant()
    
        private static Instant OTHER_REFERENCE_DATE_TIME = LocalDateTime.of(2018, 4, 2, 10, 0)
            .atZone(systemDefault())
            .toInstant()
    
        Clock clock = Stub()
    
        def "should return different date"() {
            given:
            def now
            clock.instant() >> { now }
            when:
            now = REFERENCE_DATE_TIME
            then:
            clock.instant() == REFERENCE_DATE_TIME
            clock.instant() == REFERENCE_DATE_TIME
    
            when:
            now = OTHER_REFERENCE_DATE_TIME
            then:
            clock.instant() == OTHER_REFERENCE_DATE_TIME
            clock.instant() == OTHER_REFERENCE_DATE_TIME
            clock.instant() == OTHER_REFERENCE_DATE_TIME
        }
    }