|
3 | 3 | [](https://github.com/matthewshirley/pact_dart/actions/workflows/ci.yml)
|
4 | 4 | [](https://codecov.io/gh/matthewshirley/pact_dart)
|
5 | 5 |
|
6 |
| -⚠️ WIP Package - API is not yet confirmed ⚠️ |
| 6 | +[Pact][pact-docs] is a contract testing tool to help you replace expensive and brittle end-to-end integration tests with fast, reliable and easy to debug unit tests. This framework provides a Dart DSL for generating Pact contracts. It implements [Pact Specification v3][pact-specification-v3] using the `Pact FFI Library`. |
7 | 7 |
|
8 |
| -This library provides a Dart DSL for generating Pact contracts. It implements [Pact Specification v3](https://github.com/pact-foundation/pact-specification/tree/version-3) by taking advantage of the pact_ffi library. |
| 8 | +## Documentation |
9 | 9 |
|
10 |
| -### Installation |
| 10 | +This readme offers an basic introduction to the library. View more documentation on Pact at https://docs.pact.io/. |
11 | 11 |
|
12 |
| -```bash |
13 |
| -dart pub add pact_dart |
| 12 | +- [Installation](#installation) |
| 13 | +- [Basic Usage](#usage) |
| 14 | +- [Consumer Documentation](./docs/consumer.md) |
| 15 | + |
| 16 | +## Need Help |
| 17 | + |
| 18 | +- [Join](<(http://slack.pact.io)>) our community [slack workspace](http://pact-foundation.slack.com/). |
| 19 | +- Stack Overflow: https://stackoverflow.com/questions/tagged/pact |
| 20 | +- Say 👋 on Twitter: [@pact_up] |
| 21 | + |
| 22 | +## Installation |
| 23 | + |
| 24 | +```shell |
| 25 | +# install pact_dart as a dev dependency |
| 26 | +dart pub add --dev pact_dart |
| 27 | + |
| 28 | +# download and install the required libraries |
14 | 29 | dart run pact_dart:install
|
| 30 | + |
| 31 | +# 🚀 now write some tests! |
15 | 32 | ```
|
16 | 33 |
|
17 |
| -### Example |
| 34 | +<details><summary>Flutter Instructions</summary> |
18 | 35 |
|
19 |
| -```dart |
20 |
| -import 'package:pact_dart/pact_dart.dart'; |
| 36 | +### Flutter Installation |
21 | 37 |
|
22 |
| -final pact = PactMockService('test-ffi-consumer','test-ffi-provider'); |
| 38 | +```bash |
| 39 | +# install pact_dart as a dev dependency |
| 40 | +flutter pub add --dev pact_dart |
23 | 41 |
|
24 |
| -pact |
25 |
| - .newInteraction() |
26 |
| - .given('a alligator exists', params: { 'name': 'Betsy' }) |
27 |
| - .andGiven('the alligators were recently fed') |
28 |
| - .uponReceiving('a request for an alligator') |
29 |
| - .withRequest('GET', '/alligator') |
30 |
| - .willRespondWith(200, body: { 'name': 'Betsy' }}); |
| 42 | +# download and install the required libraries |
| 43 | +flutter pub run pact_dart:install |
31 | 44 |
|
32 |
| -pact.run(secure: false); |
| 45 | +# 🚀 now write some tests! |
| 46 | +``` |
| 47 | + |
| 48 | +</details> |
33 | 49 |
|
34 |
| -final uri = Uri.parse('http://localhost:1235/alligator'); |
35 |
| -final res = await http.get(uri); |
| 50 | +<details><summary>Manual Installation Instructions</summary> |
36 | 51 |
|
37 |
| -expect(jsonDecode(res.body)['name'], equals('Betsy')); |
| 52 | +### Modify Library Location |
38 | 53 |
|
39 |
| -pact.writePactFile(overwrite: true); |
| 54 | +By default, the `Pact FFI Library` is installed to `/usr/local/lib` on macOS and Linux. However, you can use the `PACT_DART_LIB_DOWNLOAD_PATH` environment variable to modify the installation path. |
| 55 | + |
| 56 | +``` |
| 57 | +PACT_DART_LIB_DOWNLOAD_PATH=/app/my-other-location dart run pact_dart:install |
40 | 58 | ```
|
41 | 59 |
|
42 |
| -### Matching |
| 60 | +### Manual Installation |
| 61 | + |
| 62 | +Download the latest `Pact FFI Library` [libraries] for your OS, and install onto a standard library search path (for example, we suggest: `/usr/local/lib` on OSX/Linux): |
| 63 | + |
| 64 | +Ensure you have the correct extension for your OS: |
43 | 65 |
|
44 |
| -`pact_dart` supports request/response [matching techniques](https://docs.pact.io/getting_started/matching/) as defined in the [Pact Specification v3](https://github.com/pact-foundation/pact-specification/tree/version-3). |
| 66 | +- For Mac OSX: `.dylib` |
| 67 | +- For Linux: `.so` |
| 68 | +- For Windows: `.dll` |
| 69 | + |
| 70 | +```sh |
| 71 | +wget https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v0.0.2/libpact_ffi-osx-x86_64.dylib.gz |
| 72 | +gunzip libpact_ffi-osx-x86_64.dylib.gz |
| 73 | +mv libpact_ffi-osx-x86_64.dylib /usr/local/lib/libpact_ffi.dylib |
| 74 | +``` |
| 75 | + |
| 76 | +</details> |
| 77 | + |
| 78 | +## Usage |
| 79 | + |
| 80 | +### Writing a Consumer test |
| 81 | + |
| 82 | +Pact is a consumer-driven contract testing tool, which is a fancy way of saying that the API `Consumer` writes a test to set out its assumptions and needs of its API `Provider`(s). By unit testing our API client with Pact, it will produce a `contract` that we can share to our `Provider` to confirm these assumptions and prevent breaking changes. |
| 83 | + |
| 84 | +In this example, we are testing the users repository that communicates with the `/users` resource of a HTTP service. The repository has a single method `fetchAll()` that will return a list of users. |
45 | 85 |
|
46 | 86 | ```dart
|
| 87 | +import 'package:pact_dart/pact_dart.dart'; |
| 88 | +
|
| 89 | +final pact = PactMockService('FlutterConsumer','APIService'); |
| 90 | +
|
47 | 91 | pact
|
48 | 92 | .newInteraction()
|
49 |
| - .uponReceiving('a request to create an alligator named betsy') |
50 |
| - .withRequest('POST', '/alligator', body: { |
51 |
| - 'alligator': { |
52 |
| - 'name': PactMatchers.EqualTo('Betsy'), |
53 |
| - 'isHungry': PactMatchers.SomethingLike(true), |
54 |
| - 'countOfTeeth': PactMatchers.IntegerLike(80), |
55 |
| - 'countOfHumansScared': PactMatchers.DecimalLike(12.5), |
56 |
| - 'favouriteFood': PactMatchers.Includes('Pineapple'), |
57 |
| - 'status': PactMatchers.Null(), |
58 |
| - 'email': PactMatchers.Term(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$', '[email protected]'), |
59 |
| - 'friends': PactMatchers.EachLike(['Beth'], min: 1, max: 5) |
60 |
| - } |
61 |
| - }).willRespondWith(201); |
62 |
| -
|
63 |
| - final uri = Uri.parse('http://localhost:1235/alligator'); |
64 |
| - final res = await http.post(uri, |
65 |
| - headers: {'Content-Type': 'application/json'}, |
66 |
| - body: jsonEncode({ |
67 |
| - 'alligator': { |
68 |
| - 'name': 'Betsy', |
69 |
| - 'isHungry': false, |
70 |
| - 'countOfTeeth': 78, |
71 |
| - 'countOfHumansScared': 100.5, |
72 |
| - 'favouriteFood': ['Pineapple', 'Kibble', 'Human'], |
73 |
| - 'status': null, |
74 |
| - |
75 |
| - 'friends': ['Beth', 'Susan', 'Graham', 'Michael', 'Chloe'] |
76 |
| - } |
| 93 | + .given('a user exists', params: {'first_name': 'Betsy', 'last_name': 'Tester'}) |
| 94 | + .andGiven('') |
| 95 | + .uponReceiving('a request for all users') |
| 96 | + .withRequest('GET', '/users') |
| 97 | + .willRespondWith(200, body: { |
| 98 | + // Matchers are used here as we care about the types and structure of the response and not the exact values. |
| 99 | + 'page': PactMatchers.SomethingLike(1), |
| 100 | + 'per_page': PactMatchers.SomethingLike(20), |
| 101 | + 'total': PactMatchers.IntegerLike(20), |
| 102 | + 'total_pages': PactMatchers.SomethingLike(3), |
| 103 | + 'data': PactMatchers.EachLike([ |
| 104 | + { |
| 105 | + 'id': PactMatchers.uuid('f3a9cf4a-92d7-4aae-a945-63a6440b528b'), |
| 106 | + 'first_name': PactMatchers.SomethingLike('Betsy'), |
| 107 | + 'last_name': PactMatchers.SomethingLike('Tester'), |
| 108 | + 'salary': PactMatchers.DecimalLike(125000.00) |
77 | 109 | }
|
78 |
| - ) |
79 |
| - ); |
| 110 | + ]) |
| 111 | + }); |
| 112 | +
|
| 113 | +pact.run(secure: false); |
| 114 | +
|
| 115 | +final loginRepository = UsersRepository(); |
| 116 | +final users = await loginRepository.fetchAll(); |
| 117 | +
|
| 118 | +expect(users.count, equals(20)); |
| 119 | +expect(users[0].first_name, equals('Betsy')); |
| 120 | +expect(users[0].last_name, equals('Tester')); |
| 121 | +
|
| 122 | +pact.writePactFile(); |
| 123 | +pact.reset(); |
80 | 124 | ```
|
81 | 125 |
|
82 |
| -### Feature support |
| 126 | +## Compatibility |
| 127 | + |
| 128 | +<details><summary>Feature Compatibility</summary> |
83 | 129 |
|
84 | 130 | | Feature | Supported |
|
85 | 131 | | ---------------------------------------------------------------------- | --------- |
|
@@ -111,3 +157,12 @@ pact
|
111 | 157 | - ✅ -- Implemented
|
112 | 158 | - 🔨 -- Partially implemented
|
113 | 159 | - ❌ -- Not implemented
|
| 160 | + |
| 161 | +</details> |
| 162 | + |
| 163 | +[pact-docs]: https://docs.pact.io |
| 164 | +[pact-specification-v3]: https://github.com/pact-foundation/pact-specification/tree/version-3 |
| 165 | +[slack]: https://slack.pact.io |
| 166 | +[pact website]: https://docs.pact.io/ |
| 167 | +[slack channel]: https://pact-foundation.slack.com |
| 168 | +[@pact_up]: https://twitter.com/pact_up |
0 commit comments