CDC Tests - Integration Tests cant be made simpler than this!

Post on 16-Apr-2017

95 views 0 download

Transcript of CDC Tests - Integration Tests cant be made simpler than this!

Pact Tests (Consumer Driven Contracts)

Based on a true (truly imaginary?) story!

Alice

• our main character in the story

• Senior Dev in FreshMock.com

• Owns a “Subscription Service”

• Friday the 13th!

What happened on Friday the 13th?

● “CustomerInfo Service”’s change

● Updates a property in the response object

● Changes “customer” (which returns a list of customers) to

“customers” (Grammatically correct though! :( )

● BUT this one letter change BROKE the “Subscription Service”

Here comes the bigger question…

Why didn't our tests catch this??

So how do you test?

● Mock the dependencies

Subscription Service Mock CustomerInfo Service

Request

Response

Unit Tests

● Similarly the “CustomerInfo Service”, has a mock client for

“Subscription Service”

● It DID fail!

● But then “I updated my test because I know this is an expected

failure! ”

Unit Tests

● Why do we still do it?

• Fast

• Easy to setup

• No Flakiness!

• Ensures functionality of a specific microservice

Integration Tests

Subscription Service CustomerInfo Service

Request

Response

Integration Tests

● Slower

● Set up time (“Stack Setup!”)

● More flakiness

● More infrastructure!

● End up doing an E2E tests

Other types of Tests•End-to-End Tests

•Manual Tests

•Again,

• Stack Setup!

• Slower (E2E tests)

• More flakiness

• Not failing fast!

Solution?

Is there a better way to identify

these kind of issues?

Pact Tests

• Also called “Contract Tests”

• Consumer Driven Contract Tests (CDC Tests)

• Tool Used: Pact • A Contract is a collection of agreements between a client (Consumer)

and an API (Provider) that describes the interactions that can take

place between them.

Pact Tests? What are these?

Subscription Service CustomerInfo Service

Request

Response

Consumer Provider

Sends response

for assertions

Pact Tests: Consumer Side

Tests

Set Expectations

Pact Mock Provider

Tests invoke code

Source Code

Sends HTTP

Requests

Sends expected response

Is it just another Mock HTTP Server?

Subscription Service CustomerInfo Service

Request

Response

Consumer Provider

The Pact File

• Every HTTP Request and Response is

captured

• Standard way of representing

interactions - JSON file

• This is shared with the Provider.

• Explains everything that a consumer

expects from the Provider - the

endpoints, query params, header and

the response objects.

Pact Tests:Provider Side

CustomerInfo Service

Replay each HTTP Request

Get real HTTP Response

ProviderPact File Compares real and

mock responses

Provider States

• Get billing address of customer

with id=777

Subscription Service CustomerInfo Service

Request

Response

Consumer Provider

Request

Response

?

state: cust_id=777 @ DB

Provider States

• Get billing address of customer

with id=777

Subscription Service CustomerInfo Service

Request

Response

Consumer Provider

Request

Response

state: cust_id=777 @ DB

state: cust_id=777 @ DB

Sharing Pact Files

Filesystem Cloud Pact BrokerCI Build Artefact

Sharing Pact Files - Pact Broker

• Pact Broker - a repo for storing pacts

• Auto-generated documentation

• The ability to tag a Pact (i.e. “prod") -

versioning pacts

• Network Graph

Sharing Pact Files - Pact Broker

Automating Pact Tests

• When a new Pact is published, how does the Provider get notified?

Manual Trigger Pact Broker

Webhook

Swagger Validator

Advantages

•Eliminates Wrong Assumptions

•Enables communication

•Very less setup time!

•No extra infrastructure

•Fast in Execution

•Fails Fast

•No flakiness!

•Easy to debug

DEMO

Freshapps DevPortal Freshapps Activities

Request

Response

Consumer Provider

Consumer Side : Setup Mock Server

Pact.service_consumer 'DevPortal' do

end

Consumer Side : Setup Mock Server

Pact.service_consumer 'DevPortal' do has_pact_with "Freshapps Activities" do

end end

Consumer Side : Setup Mock Server

Pact.service_consumer 'DevPortal' do has_pact_with "Freshapps Activities" do mock_service :freshapps_activities do port 3005 end end end

Consumer Side : Setup Expectations

freshapps_activities.given(“all activities without role param")

Consumer Side : Setup Expectations

freshapps_activities.given(“all activities without role param") .upon_receiving("a request for all activities") .with( method: :get, path: '/all/activities.json', query: { page: '1', account_id: '1' }, headers: {'Content-Type' => 'application/json', 'Accept' => 'application/json'} )

Consumer Side : Setup Expectations

freshapps_activities.given(“all activities without role param") .upon_receiving("a request for all activities") .with( method: :get, path: '/all/activities.json', query: { page: '1', account_id: '1' }, headers: {'Content-Type' => 'application/json', 'Accept' => 'application/json'} ) .will_respond_with( status: 422, headers: { 'Content-Type' => 'application/json; charset=utf-8' }, body: { "error_msg" => "Required parameter missing" } )

Consumer Side : Make Request & Assert

resp = FreshappsActivitiesClient.get_activities(path, query, header)

Consumer Side : Make Request & Assert

resp = FreshappsActivitiesClient.get_activities(path, query, header) expected_response = { "error_msg" => "Required parameter missing" } expect(resp.parsed_response.inspect).to eq(expected_response.inspect)

Generated Pact File { "consumer": { "name": "DevPortal" }, "provider": { "name": "Freshapps Activities" }, "interactions": [ { "description": "a request for all activities", "provider_state": "all activities without role param", "request": { "method": "get", "path": "/all/activities.json", "query": "page=1&account_id=1", "headers": { "Content-Type": "application/json", "Accept": "application/json" } }, "response": { "status": 422, "headers": { "Content-Type": "application/json; charset=utf-8" }, "body": { "error_msg": "Required parameter missing" } } } ], "metadata": { "pactSpecificationVersion": "1.0.0" } }

Provider Side : Make Request & Assert

Pact.service_provider 'Freshapps Activities' do

end

Provider Side : Make Request & Assert

Pact.service_provider 'Freshapps Activities' do honours_pact_with 'DevPortal' do end end

Provider Side : Make Request & Assert

Pact.service_provider 'Freshapps Activities' do honours_pact_with 'DevPortal' do pact_uri "../freshapps_devportal/spec/pacts/devportal-freshapps_activities.json" end end end

Provider Side : Setup Test Data

Pact.provider_states_for "DevPortal" do provider_state "all activities without role param" do

end end

Remember: Consumer Assumed:

freshapps_activities.given(“all activities without role

param")

Provider Side : Setup Test Data

Pact.provider_states_for "DevPortal" do provider_state "all activities without role param" do set_up do factory :activity do account_id 1 user_id 1 extension_id 2 version_id 2 activity_type 'Extension' visibility 1 activity { 'extension_id' => '2', 'name' => 'Sample Extension’} end end end end

Remember: Consumer Assumed:

freshapps_activities.given(“all activities without role

param")

Provider Side : Pact Verify Failure

Provider Side : Pact Verify Success

pact.io

• Working examples and documentations available at pact.io

• Supports: Ruby, Java, .NET

• Beta: JS,Python, Swift, Go

• https://groups.google.com/forum/#!forum/pact-support

• https://gitter.im/realestate-com-au/pact

• Other Similar Tools:

• Pacto (by Thoughtworks - but not maintained though.)

References

● To know about Consumer Driven Contract: https://

martinfowler.com/articles/consumerDrivenContracts.html

● https://www.thoughtworks.com/radar/techniques/

consumer-driven-contract-testing

And… What happened to Alice?

Oh yea! She implemented “Pact Tests”

and

lived happily ever-after!!

Questions

Thank You!