This version is still in development and is not considered stable yet. For the latest stable version, please use spring-cloud-contract 4.1.3!

Consumer-Driven Contracts: Stubs Per Consumer

There are cases in which two consumers of the same endpoint want to have two different responses.

This approach also lets you immediately know which consumer uses which part of your API. You can remove part of a response that your API produces and see which of your autogenerated tests fails. If none fails, you can safely delete that part of the response, because nobody uses it.

Consider the following example of a contract defined for the producer called producer, which has two consumers (foo-consumer and bar-consumer):

Consumer foo-service
request {
   url '/foo'
   method GET()
}
response {
    status OK()
    body(
       foo: "foo"
    }
}
Consumer bar-service
request {
   url '/bar'
   method GET()
}
response {
    status OK()
    body(
       bar: "bar"
    }
}

You cannot produce two different responses for the same request. That is why you can properly package the contracts and then profit from the stubsPerConsumer feature.

On the producer side, the consumers can have a folder that contains contracts related only to them. By setting the stubrunner.stubs-per-consumer flag to true, we no longer register all stubs but only those that correspond to the consumer application’s name. In other words, we scan the path of every stub and, if it contains a subfolder with name of the consumer in the path, only then is it registered.

On the foo producer side the contracts would look like this

.
└── contracts
    ├── bar-consumer
    │   ├── bookReturnedForBar.groovy
    │   └── shouldCallBar.groovy
    └── foo-consumer
        ├── bookReturnedForFoo.groovy
        └── shouldCallFoo.groovy

The bar-consumer consumer can either set the spring.application.name or the stubrunner.consumer-name to bar-consumer Alternatively, you can set the test as follows:

@SpringBootTest(classes = Config, properties = ["spring.application.name=bar-consumer",
		"stubrunner.jms.enabled=false"])
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
		repositoryRoot = "classpath:m2repo/repository/",
		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
		stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerSpec {
...
}

Then only the stubs registered under a path that contains bar-consumer in its name (that is, those from the src/test/resources/contracts/bar-consumer/some/contracts/…​ folder) are allowed to be referenced.

You can also set the consumer name explicitly, as follows:

@SpringBootTest(classes = Config, properties = "stubrunner.jms.enabled=false")
@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
        repositoryRoot = "classpath:m2repo/repository/",
        consumerName = "foo-consumer",
        stubsMode = StubRunnerProperties.StubsMode.REMOTE,
        stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerWithConsumerNameSpec {
...
}

Then only the stubs registered under a path that contains the foo-consumer in its name (that is, those from the src/test/resources/contracts/foo-consumer/some/contracts/…​ folder) are allowed to be referenced.

For more information about the reasons behind this change, see issue 224.