SGT Grumpy Pants
SGT Grumpy Pants

Reputation: 4436

Data Driven Spock Tests

I have the following class that I want to test

package bsg

class NotificationService {

    def create(String publicMsg, String privateMsg = null, Player activePlayer, Player passivePlayer = null) {
        new Notification(
                game: activePlayer.game, publicMessage: publicMsg, privateMessage: privateMsg,
                activePlayer: activePlayer, passivePlayer: passivePlayer
                ).save(failOnError: true)
    }

}

And the following test class

package bsg

import grails.test.mixin.*
import spock.lang.Specification
import spock.lang.Unroll

/**
 * See the API for {@link grails.test.mixin.services.ServiceUnitTestMixin} for usage instructions
 */
@TestFor(NotificationService)
@Mock([Notification, Game, Player])
class NotificationServiceSpec extends Specification {

    NotificationService service

    private static final String HIDDEN_SIDE = "1 - Launch Scout"
    private static Player adama
    private static Player baltar
    private static Game game
    private static Card card
    private static NotificationService notificationService

    def setup() {
        service = new NotificationService()
        game = new Game(name: "foo").save(validate:false)
        card = new Card(hiddenSide: HIDDEN_SIDE, backSide: "Bacon", deck: new Deck(backSide: "Skill Card"))
        adama = new Player(game: game, character:Player.Character.Adama).save(validate: false)
        baltar = new Player(game: game, character:Player.Character.Baltar).save(validate: false)
        notificationService = service
    }

    @Unroll
    def "test create1"(String privateMsg, Player passivePlayer, Closure serviceMethod) {
        when: "I create a notification"
        Notification notification = serviceMethod.call()

        then: "a notification has been persisted"
        notification?.id != null

        and: "it displays the correct messages"
        notification.publicMessage == "foo"
        notification.privateMessage == privateMsg
        notification.activePlayer == adama
        notification.passivePlayer == passivePlayer

        where:
        privateMsg  | passivePlayer | serviceMethod
        "bar"       | baltar        | { notificationService.create("foo", "bar", adama, baltar) }
        null        | baltar        | { notificationService.create("foo", null, adama, baltar) }
        "bar"       | null          | { notificationService.create("foo", "bar", adama) }
        null        | null          | { notificationService.create("foo", adama) }
    }

    @Unroll
    def "test create2"(String privateMsg, Player passivePlayer, Closure serviceMethod) {
        when: "I create a notification"
        Notification notification = serviceMethod.call()

        then: "a notification has been persisted"
        notification?.id != null

        and: "it displays the correct messages"
        notification.publicMessage == "foo"
        notification.privateMessage == privateMsg
        notification.activePlayer == adama
        notification.passivePlayer == passivePlayer

        where:
        privateMsg  | passivePlayer | serviceMethod
        "bar"       | baltar        | { notificationService.create("foo", "bar", adama, baltar) }
        null        | baltar        | { notificationService.create("foo", null, adama, baltar) }
        "bar"       | null          | { notificationService.create("foo", "bar", adama) }
        null        | null          | { notificationService.create("foo", adama) }
    }
}

Both tests are identical, but in "test create1"() the first 2 tests fail and the second two pass. In "test create2"() all 4 tests pass. The error message for both failures is this:

Condition not satisfied:

notification.passivePlayer == passivePlayer
|            |             |  |
|            Baltar (null) |  null
|                          false
Adama (null) foo to Baltar (null)

at bsg.NotificationServiceSpec.test create1(NotificationServiceSpec.groovy:44)

So it looks like the static class variable baltar is somehow not set for the first tests. Can someone help me understand what's going on?

Upvotes: 0

Views: 699

Answers (1)

SGT Grumpy Pants
SGT Grumpy Pants

Reputation: 4436

Well, funny story... I'm at the NFJS RWX/CDX conference and immediately after posting this question I got in the elevator with Peter Niederwieser. He had me sorted out by the time we got to the bottom floor.

It makes perfect sense if you stop to think about it for a minute. I'm sure I'll get the specifics wrong, but my basic understanding is that Spock builds the actual junit test methods from the data tables before the setup() method executes. Thankfully, using the setupSpec() will accomplish what I'm looking for.

So this is what I did to fix it. Thanks, Peter.

...
private static final String HIDDEN_SIDE = "1 - Launch Scout"
private static Player adama
private static Player baltar
private static Game game
private static Card card
private static NotificationService notificationService

def setup() {
    notificationService = new NotificationService()

    game.save(validate:false)
    adama.save(validate: false)
    baltar.save(validate: false)
}

def setupSpec() {
    game = new Game(name: "foo")
    card = new Card(hiddenSide: HIDDEN_SIDE, backSide: "Bacon", deck: new Deck(backSide: "Skill Card"))
    adama = new Player(game: game, character:Player.Character.Adama)
    baltar = new Player(game: game, character:Player.Character.Baltar)
}
...

Peter said he would jump on and answer this later, I'm sure that he'll have a MUCH better explanation. Hopefully, he'll have time; and I will accept his answer if he does.

Upvotes: 1

Related Questions