All of the following code will be used exclusively for Providers. This will run the Pacts against the real Provider and either verify or fail validation on the Pact Broker.
Create a single unit test function. This will test all defined consumers of the service.
protected function setUp(): void
{
// Start API
}
protected function tearDown(): void
{
// Stop API
}
public function testPactVerifyConsumers(): void
{
$config = new VerifierConfig();
$config->getProviderInfo()
->setName('someProvider')
->setHost('localhost')
->setPort(8000);
$config->getProviderState()
->setStateChangeUrl(new Uri('http://localhost:8000/pact-change-state'))
->setStateChangeTeardown(true)
->setStateChangeAsBody(true);
// If your provider dispatch messages
$config->addProviderTransport(
(new ProviderTransport())
->setProtocol(ProviderTransport::MESSAGE_PROTOCOL)
->setPort(8000)
->setPath('/pact-messages')
->setScheme('http')
);
// If you want to publish verification results to Pact Broker.
if ($isCi = getenv('CI')) {
$publishOptions = new PublishOptions();
$publishOptions
->setProviderVersion(exec('git rev-parse --short HEAD'))
->setProviderBranch(exec('git rev-parse --abbrev-ref HEAD'));
$config->setPublishOptions($publishOptions);
}
// If you want to display more/less verification logs.
if ($logLevel = \getenv('PACT_LOGLEVEL')) {
$config->setLogLevel($logLevel);
}
// Add sources ...
$verifyResult = $verifier->verify();
$this->assertTrue($verifyResult);
}
Sometimes you may need to add custom headers to the requests that can't be persisted in a pact file.
e.g. an OAuth bearer token: Authorization: Bearer 1a2b3c4d5e6f7g8h9i0k
$config->getCustomHeaders()
->addHeader('Authorization', 'Bearer 1a2b3c4d5e6f7g8h9i0k');
The requests will have custom headers added before being sent to the Provider API.
Note: Custom headers are not the only approach for authentication and authorization. For other approaches, please refer to this documentation.
Important Note: You should only use this feature for headers that can not be persisted in the pact file. By modifying the request, you are potentially modifying the contract from the consumer tests!
There are four ways to verify Pact files. See the examples below.
This will grab the Pact files from a Pact Broker.
$selectors = (new ConsumerVersionSelectors())
->addSelector(new Selector(mainBranch: true))
->addSelector(new Selector(deployedOrReleased: true));
$broker = new Broker();
$broker
->setUrl(new Uri('http://localhost'))
->setUsername('user')
->setPassword('pass')
->setToken('token')
->setEnablePending(true)
->setIncludeWipPactSince('2020-01-30')
->setProviderTags(['prod'])
->setProviderBranch('main')
->setConsumerVersionSelectors($selectors)
->setConsumerVersionTags(['dev']);
$verifier->addBroker($broker);
This will grab the Pact file from a url.
$url = new Url();
$url
->setUrl(new Uri('http://localhost:9292/pacts/provider/personProvider/consumer/personConsumer/latest'))
->setUsername('user')
->setPassword('pass')
->setToken('token');
$verifier->addUrl($url);
This will grab local Pact files in directory. Results will not be published.
$verifier->addDirectory('C:\SomePath');
This will grab local Pact file. Results will not be published.
$verifier->addFile('C:\SomePath\consumer-provider.json');
You can use the built in PHP server to accomplish this during your tests setUp function. The Symfony Process library can be used to run the process asynchronous.
The PACT verifier is a wrapper of the Ruby Standalone Verifier. See API with Provider States for more information on how this works. Since most PHP rest APIs are stateless, this required some thought.
Here are some options:
- Write the posted state to a file and use a factory to decide which mock repository class to use based on the state.
- Set up your database to meet the expectations of the request. At the start of each request, you should first reset the database to its original state.
No matter which direction you go, you will have to modify something outside of the PHP process because each request to your server will be stateless and independent.