pact-foundation / pact-jvm

JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
https://docs.pact.io
Apache License 2.0
1.07k stars 475 forks source link

Spock framework support for consumer/provider #1384

Open naushadamin opened 3 years ago

naushadamin commented 3 years ago

Does Pact support Spock framework out of the box to write consumer and provider side tests? If yes then do you have examples/reference documentation.

uglyog commented 3 years ago

There is no direct support (as in a Spock plugin), but it is fairly easy to use Spock for your tests. In fact, most of Pact-JVM is tested with Spock.

Consumer tests can use the Groovy DSL. For examples see https://github.com/pact-foundation/pact-jvm/blob/master/consumer/groovy/src/test/groovy/au/com/dius/pact/consumer/groovy/ValuesMatcherPactSpec.groovy and https://github.com/pact-foundation/pact-jvm/blob/master/consumer/groovy/src/test/groovy/au/com/dius/pact/consumer/groovy/messaging/PactMessageBuilderSpec.groovy

For provider tests, you can use the Spock data fixture mechanism to load the Pact files for the test, and then call the Verifier for each one. I will try find an example.

uglyog commented 3 years ago

Here is an example of a provider test. Note this is using a very old version of Pact-JVM, and will need to be updated to work with the latest.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Transactional
@SuppressWarnings('PrivateFieldCouldBeFinal')
@ActiveProfiles('test')
class PactVerificationSpec extends Specification {
  private static String pactBrokerUser
  private static String pactBrokerPassword
  private static ProviderInfo retailerService = new ProviderInfo(name: 'retailer', stateChangeTeardown: true)
  private static ProviderVerifier verifier = new ProviderVerifier()

  @Autowired
  private DataSource dataSource

  @LocalServerPort
  Integer port

  private stateChangeHandler = { ProviderState state, String action ->
    def sql = new Sql(dataSource)
    PactVerificationSpec.getResourceAsStream("/sql/pact/${state.name.replaceAll(/\s+/, '_')}_${action}.sql").eachLine {
      sql.execute(it)
    }
    sql.close()
    true
  }

  def setupSpec() {
    pactBrokerUser = System.properties['pactBrokerUser']
    pactBrokerPassword = System.properties['pactBrokerPassword']
  }

  def setup() {
    retailerService.port = port

    verifier.providerVersion = { System.getProperty('projectVersion') }
  }

  @Unroll
  def 'pact test with consumer #consumer.name'() {
    expect:
    !verifyConsumerPact(consumer)

    where:
    consumer << loadPactsForProvider().collect { ConsumerInfo.from(it) }
  }

  private boolean verifyConsumerPact(ConsumerInfo consumer) {
    consumer.stateChange = stateChangeHandler
    retailerService.consumers = [consumer]
    def result = verifier.verifyProvider(retailerService)
    if (result) {
      verifier.displayFailures(result)
    }
    result
  }

  private static loadPactsForProvider() {
    def pactBrokerClient = new PactBrokerClient("https://${System.properties['pactBrokerHost'] }",
        [authentication: ['basic', pactBrokerUser, pactBrokerPassword]])
    def branch = System.getenv('BRANCH')?.toLowerCase()
    def consumers = null

    if (branch != null && branch != 'master') {
      consumers = pactBrokerClient.fetchConsumersWithTag(retailerService.name, branch)
    }

    consumers ?: pactBrokerClient.fetchConsumers(retailerService.name)
  }
}