- Notifications
You must be signed in to change notification settings - Fork43
A Swift / ObjeciveC DSL for creating pacts.
License
DiUS/pact-consumer-swift
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ℹ
A new version featuringPact Specification v3, a simplified installation and better management of the mock server processes is in active development and can be found atPactSwift. We are currently looking for people to try it out and provide feedback.
This library provides a Swift / Objective C DSL for creating ConsumerPacts. It provides support forConsumer Driven Contract Testing between dependent systems where the integration is based on HTTP (or message queues for some of the implementations).
But why? To test communication boundaries between your app and services.You can view a presentation on how Pact can work in a mobile context here:Yow! Connected 2016 Andrew Spinks - Increasing The Confidence In Your Service Integrations.
ImplementsPact Specification v2,includingflexible matching.
This DSL relies on the Rubypact-ruby-standalone (brew tap) to provide the mock service for the tests.
Note: seeUpgrading for notes on upgrading from 0.2 to 0.3
brew tap pact-foundation/pact-ruby-standalonebrew install pact-ruby-standalone
This will install the following tools:
pactpact-brokerpact-messagepact-mock-servicepact-provider-verifierpact-publishpact-stub-service
Alternatively you can download and install thepact-ruby-standalone archives for your platform and install as per installation instructions written inPact Ruby Standalone release notes.
In Xcode, edit your scheme and addpre- andpost-actions toTest
to start and stoppact-mock-service
. Make sure you select your target inProvide build settings from the drop down menu.
# Pre-actionsPATH=/path/to/your/standalone/pact/bin:$PATHpact-mock-service start --pact-specification-version 2.0.0 --log "${SRCROOT}/tmp/pact.log" --pact-dir "${SRCROOT}/tmp/pacts" -p 1234# Post-actionsPATH=/path/to/your/standalone/pact/bin:$PATHpact-mock-service stop
Note: your generated Pact files will be dropped into"${SRCROOT}/tmp/pacts"
folder.
UsingCarthage
- See thePactSwiftExample
for an example project using
pact-consumer-swift
with Carthage for an iOS target. - See thePactMacOSExample
for an example project using
pact-consumer-swift
through Carthage for a macOS target.
UsingCocoaPods
- See thePactObjectiveCExample
for an example project using
pact-consumer-swift
with CocoaPods for an iOS target.
- See thePactSwiftPMExample
for an example project using
pact-consumer-swift
library through Swift Package Manager for an executable that runs in terminal.
Write a Unit test similar to the following (NB: this example is using theQuick test framework)
import PactConsumerSwift...beforeEach{ animalMockService=MockService(provider:"Animal Service", consumer:"Animal Consumer Swift") animalServiceClient=AnimalServiceClient(baseUrl: animalMockService!.baseUrl)}it("gets an alligator"){ animalMockService!.given("an alligator exists").uponReceiving("a request for an alligator").withRequest(method:.GET, path:"/alligator").willRespondWith(status:200, headers:["Content-Type":"application/json"], body:["name":"Mary"]) //Run the tests animalMockService!.run{(testComplete)->Voidin animalServiceClient!.getAlligator{(alligator)inexpect(alligator.name).to(equal("Mary"))testComplete()}}}
An optionaltimeout
(seconds) parameter can be included on the run function. This defaults to 30 seconds.
... animalMockService!.run(timeout:60){(testComplete)->Voidin animalServiceClient!.getAlligator{(alligator)inexpect(alligator.name).to(equal("Mary"))testComplete()}}
Write a Unit test similar to the following
@import PactConsumerSwift;...- (void)setUp { [supersetUp]; self.animalMockService = [[MockServicealloc]initWithProvider:@"Animal Provider"consumer:@"Animal Service Client Objective-C"]; self.animalServiceClient = [[OCAnimalServiceClientalloc]initWithBaseUrl:self.animalMockService.baseUrl];}- (void)testGetAlligator {typedefvoid (^CompleteBlock)(); [[[[self.animalMockServicegiven:@"an alligator exists"]uponReceiving:@"oc a request for an alligator"]withRequestHTTPMethod:PactHTTPMethodGETpath:@"/alligator"query:nilheaders:nilbody:nil]willRespondWithHTTPStatus:200headers:@{@"Content-Type":@"application/json"}body:@"{\"name\":\"Mary\"}" ]; [self.animalMockServicerun:^(CompleteBlock testComplete) { Animal *animal = [self.animalServiceClientgetAlligator];XCTAssertEqualObjects(animal.name,@"Mary");testComplete(); }];}
An optionaltimeout
(seconds) parameter can be included on the run function. This defaults to 30 seconds.
... [self.animalMockServicerun:^(CompleteBlock testComplete) { Animal *animal = [self.animalServiceClientgetAlligator];XCTAssertEqualObjects(animal.name,@"Mary");testComplete(); }timeout:60];}
Write a Unit Test similar to the following:
import PactConsumerSwift...varanimalMockService:MockService?varanimalServiceClient:AnimalServiceClient?overridefunc setUp(){ super.setUp() animalMockService=MockService(provider:"Animal Provider", consumer:"Animal Service Client") animalServiceClient=AnimalServiceClient(baseUrl: animalMockService!.baseUrl)}func testItGetsAlligator(){ // Prepare the expecated behaviour using pact's MockService animalMockService!.given("an alligator exists").uponReceiving("a request for alligator").withRequest(method:.GET, path:"/alligator").willRespondWith(status:200, headers:["Content-Type":"application/json"], body:["name":"Mary"]) // Run the test animalMockService!.run(timeout:60){(testComplete)->Voidinself.animalServiceClient!.getAlligator{(response)->inXCTAssertEqual(response.name,"Mary")testComplete()}}}...
An optionaltimeout
(seconds) parameter can be included on the run function. Defaults to 30 seconds.
... // Run the test animalMockService!.run(timeout:60){(testComplete)->Voidinself.animalServiceClient!.getAlligator{(response)->inXCTAssertEqual(response.name,"Mary")testComplete()}}
For an example on how to test overhttps
seePactSSLSpec.swift.
In addition to verbatim value matching, you have 3 useful matching functionsin theMatcher
class that can increase expressiveness and reduce brittle testcases.
Matcher.term(matcher, generate)
- tells Pact that the value should match usinga given regular expression, usinggenerate
in mock responses.generate
must bea string.Matcher.somethingLike(content)
- tells Pact that the value itself is not important, as longas the elementtype (valid JSON number, string, object etc.) itself matches.Matcher.eachLike(content, min)
- tells Pact that the value should be an array type,consisting of elements like those passed in.min
must be >= 1.content
maybe a valid JSON value: e.g. strings, numbers and objects.
NOTE: One caveat to note, is that you will need to use valid Rubyregular expressions and double escape backslashes.
See thePactSpecs.swift
,PactObjectiveCTests.m
for examples on how to expect error responses, how to use query params, and Matchers.
For more on request / response matching, see [Matching][getting_started/matching].
Xcode'spre-actions andpost-actions do not honour non-zero script exits and therefore would not fail your build if publishing to a Pact Broker would fail. If you would like to upload your Pact files to a Pact Broker as part of your CI, we would suggest that you create a separate step in your CI workflow with that responsibility.
Seepact-ruby-standalone page for installation instructions and how to usepact-broker
client.
If your setup is correct and your tests run against the pack mock server, then you should see a log file here:$YOUR_PROJECT/tmp/pact.log
And the generated pacts here:$YOUR_PROJECT/tmp/pacts/...
Publish your generated pact file(s) to yourPact Broker or aHosted Pact Broker so yourAPI provider can always retrieve them from one location, even when pacts change. Or even just by simply sending the pact file to your API provider devs so they can used them in their tests of their API responses. SeeVerifying pacts for more information.For an end-to-end example with a ruby back end service, have a look at theKatKit example.
Also, check out this article onusing a dockerized Node.js service that uses provider states.
- The Pact websitePact
- The pact mock server that the Swift library uses under the hoodPact mock service
- A pact broker for managing the generated pact files (so you don't have to manually copy them around!)Pact broker
Please readCONTRIBUTING.md
About
A Swift / ObjeciveC DSL for creating pacts.