06 Aug 2014

Spock Lifecycle and non-intuitive WHERE block

Résumé: as it appeared Spock’s Lifecycle is not always intuitive, this is especially true for where block. Even though it’s located inside of a test method, its execution happens outside and what’s even more peculiar - it’s invoked even before setup() happens or fields get initialized.

Spock allows you writing data-driven tests in a very easy manner using where:

def "square function"(int a, int square) {
        expect:
            square == a * a
        where:
            a | square
           -1 | 1
            0 | 0
            1 | 1
}

But docs keep silence on how this magic is implemented. Spock’s Syntax is not pure Groovy, its code gets transformed with Groovy AST. Depending on the result of these transformations we may or may not use some features of Spock.

First, where is going to be factored out into a separate method within the same test class. But what’s really interesting - it will be invoked before setup(). The whole thing is here:

This means that variables that are initialized in setup() won’t be seen in where:

class SomeTest extends Specification {
    int a = 2
    int b = 1

    def setup() {
        b = 1
    }
    def someTest(a, b){
      expect:
        b != a
      where:
        a << [a]
        b << [b]
    }
}

In this test we’ll get a == b == 0 which demonstrates the point.

As a consequence all the JUnit Listeners (after all Spock is just an extension of JUnit) that observe beforeTestMethod will also be triggered after the data is initialized in where. This includes DependencyInjectionTestExecutionListener from Spring TestContext. Which means that if you want to use @Autowired fields which are injected from the context, you won’t be able to use them in where, what you’ll get there is null.

Read more
18 Jun 2014

Spock Lifecycle и неинтуитивный блок where

Резюме: как оказывается у Spock’а порою не совсем интуитивный жизненный цикл, особенно это касается блока where который хоть и находится внутри тестового метода, а выполняется не то что до самого метода (что было бы логично), а даже до setup() и до инициализации полей класса.

Spock позволяет достаточно удобно строить data-driven тесты с помощью своего where:

def "square function"(int a, int square) {
        expect:
            square == a * a
        where:
            a | square
           -1 | 1
            0 | 0
            1 | 1
}

Нигде только не упоминается о том как же в итоге это все реализуется. Синтаксис используемый Spock’ом на самом деле не является чистым Groovy, этот код потом трансформируется с помощью Groovy AST. Так вот от того во что он трансформируется зависит возможность использования некоторых фич Spock’a.

Так, например, where будет вынесено отдельным методом в тот же класс и, что интересно, будет вызван до setup(). Цепочку можно проследить тут:

Это значит так же что переменные которые инициализируются в setup() не будут видны в where:

class SomeTest extends Specification {
    int a = 2
    int b = 1

    def setup() {
        b = 1
    }
    def someTest(a, b){
      expect:
        b != a
      where:
        a << [a]
        b << [b]
    }
}

И в итоге мы все равно получим в тесте a == b == 0.

Из этого так же следует, что все слушатели, которые навешаны на beforeTestMethod тоже отработают после того как данные будут инициализированы в where. К таким слушателям относятся и DependencyInjectionTestExecutionListener из Spring TestContext. Т.е. если вы захотите использовать @Autowired поля которые инжектятся из контекста, в where вы их использовать не сможете, там у вас все равно будут null.

Read more