consumer-driven contracts with pact and php

17
@andykelk | #SydPHP Consumer-driven Contracts with Pact and PHP

Upload: andy-kelk

Post on 16-Jan-2017

770 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Consumer-driven Contracts with Pact and PHP

Page 2: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

provider

consumer 1

consumer 2

consumer 3

{ status: “ok”, count: 20, items: [ { id: 10000, name: ...

Providers and consumers

expectations

expectations

expectations

Page 3: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Consumer-driven contracts

• In a service oriented architecture, a service provider typically has many consumers.

• Each of those consumers has expectations about the service.

• Over time, consumer expectations change and the provider must evolve to meet them.

• Standard approaches to this introduce interdependence into the relationship.

• The Consumer-Driven Contracts pattern solves that.

Page 4: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Consumer-driven contracts

provider

consumer 1

consumer 2

consumer 3

{ status: “ok”, count: 20, items: [ { id: 10000, name: ...

CONTR

ACT

CONTR

ACT

CONTR

ACT

Page 5: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Pact

• Ruby gem which implements automated testing for consumer-driven contracts

• There are also implementations for• .Net (Provider, Consumer)• JVM (Provider, Consumer)• JavaScript (Provider, Consumer)• Swift (Consumer)

Page 6: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Pact

https://github.com/realestate-com-au/pact

Page 7: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Pact

• BUT… no PHP implementation 😞 • Luckily we can use the Provider Proxy to help us test a PHP

service provider

Page 8: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

JavaScript Consumer

Jasmine

PhantomJS

PACT

Karma

Test framework/Test runner

Browser (headless)

Mock/contract endpoint

App code (HTTP client,

model)Production code

exercises

invokes via socket

HTTP Api call

creates

pact json file

Page 9: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

beforeEach(function() { client = ZooClient.createClient('http://localhost:1234');

alligatorProvider = Pact.mockService({ consumer: 'Alligator Consumer', provider: 'Alligator Provider', port: 1234, done: function (error) { expect(error).toBe(null); } }); });

Setting up a JavaScript consumer

Page 10: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Setting up a JavaScript consumer it("should return an alligator", function(done) { alligatorProvider .given("an alligator with the name Mary exists") .uponReceiving("a request for an alligator") .withRequest("get", "/alligators/Mary", { "Accept": "application/json" }).willRespondWith(200, { "Content-Type": "application/json" }, { "name": "Mary" });

alligatorProvider.run(done, function(runComplete) { expect(client.getAlligatorByName("Mary")).toEqual(new Alligator("Mary")); runComplete(); }); });

Page 11: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Now we have a pact{ "consumer": { "name": "Alligator Consumer" }, "provider": { "name": "Alligator Provider" }, "interactions": [ { "description": "a request for an alligator", "provider_state": "an alligator with the name Mary exists", "request": { "method": "get", "path": "/alligators/Mary", "headers": { "Accept": "application/json" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "name": "Mary" } } } ], "metadata": { "pactSpecificationVersion": "1.0.0" }}

Page 12: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

PHP Provider

RakePACTproxy

Test framework/Test runner

Mock/contract endpoint

App code (service

provider)

Production code

invokes

pact json file

reads

validates

Page 13: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Create the API provider<?phprequire 'vendor/autoload.php';$app = new \Slim\Slim();$app->get('/alligators/:name', function ($name) use ($app) { $app->response->setStatus(200); $app->response()->headers->set('Content-Type', 'application/json'); echo json_encode(array('name' => $name));});$app->run();

Page 14: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Use rake and pact provider proxy to testrequire 'pact/provider/proxy/tasks'

Pact::ProxyVerificationTask.new :alligator do | task | task.pact_url './spec/pacts/alligator_consumer-alligator_provider.json', :pact_helper => './spec/support/alligator_pact_helper' task.provider_base_url 'http://localhost:8000'end

Pact.provider_states_for "Alligator Consumer" do provider_state "an alligator with the name Mary exists" do set_up do # Set-up the provider state (e.g. create fixture) here end endend

Page 15: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Demo Time!

Page 16: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

What’s next?

• PHP implementation of the pact specification• Use pact broker to share pact files and provide:• Auto-generated documentation• Dynamically generated network diagrams• The ability to tag a pact (ie. "prod") so a provider can

verify itself against a fixed version of a pact to ensure backwards compatibility.

• Webhooks to trigger provider builds when a pact is published.

Page 17: Consumer-driven contracts with Pact and PHP

@andykelk | #SydPHP

Questions?

• Further reading:• http://martinfowler.com/articles/consumerDrivenContracts.html• https://github.com/realestate-com-au/pact• http://techblog.realestate.com.au/testing-interactions-with-web-services-with

out-integration-tests-in-ruby/• http://techblog.realestate.com.au/enter-the-pact-matrix-or-how-to-decouple-t

he-release-cycles-of-your-microservices/• Code from the demo:

• https://github.com/mopoke/pact-php-demo• Relive this talk in blog form:

• http://www.andykelk.net/tech/consumer-driven-contracts-with-pact-and-php