Vikash Tiwari
Vikash Tiwari

Reputation: 113

Set-up/clean-up only once per feature method with 'where:' block

Spock has setupSpec on the Spec class level. I would want to have something similar for a single test case level.

This might not be available in Spock, Does someone has a workaround for this.

void "test something"() {
    setup:
    User user = createUser()

    when:
    user.setAdress(new Address(zipCode: inputZipCode, city: inputCity))

    then:
    user.address.city == inputCity
    user.address.zipCode == inputZipCode

    cleanup:
    deleteUser(user)

    where:
    inputCity | inputZipCode
    "a" |"1"
    "b" |"2"
}

Creating and deleting user is unnecessarily done after every iteration.

Upvotes: 1

Views: 427

Answers (1)

kriegaex
kriegaex

Reputation: 67317

I think this is very ugly because it involves manual bookkeeping. I do not recommend you to do it like this, but anyway:

package de.scrum_master.stackoverflow.q57721328

import spock.lang.See
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

class OneTimeSetupCleanupParametrisedTest extends Specification {
  @Shared User user
  @Shared int iteration

  User createUser() {
    // Set up test fixture only if iteration == 0 (or optionally, if fixture is uninitialised) 
    user ?: new User()
  }

  void deleteUser(User userInstance) {
    // Clean up counter (and test fixture, if any) only if iteration == total number of iterations
    if (++iteration == specificationContext.currentIteration.estimatedNumIterations) {
      userInstance.delete()
      user = null
      iteration = 0
    }
  }

//  @Unroll
  void "test something"() {
    setup:
    // Call initialiser helper for each iteration, relying on the fact that it will only
    // create a text fixture if none exists yet
    user = createUser()

    when:
    user.setAdress(new Address(zipCode: inputZipCode, city: inputCity))

    then:
    user.address.city == inputCity
    user.address.zipCode == inputZipCode

    cleanup:
    // Call clean-up helper for each iteration, relying on the fact that it will only
    // clean up the fixture during the last iteration
    deleteUser(user)

    where:
    inputCity | inputZipCode
    "a"       | "1"
    "b"       | "2"
  }

  static class User {
    Address address

    User() {
      println "creating user"
    }

    void setAdress(Address address) {
      this.address = address
    }

    void delete() {
      println "deleting user"
      address = null
    }
  }

  static class Address {
    String zipCode, city
  }
}

Console log:

creating user
deleting user

Update: The Spock manual says about this topic:

Sharing of Objects between Iterations

In order to share an object between iterations, it has to be kept in a @Shared or static field.

NOTE: Only @Shared and static variables can be accessed from within a where: block.

Note that such objects will also be shared with other methods. There is currently no good way to share an object just between iterations of the same method. If you consider this a problem, consider putting each method into a separate spec, all of which can be kept in the same file. This achieves better isolation at the cost of some boilerplate code.

Upvotes: 2

Related Questions