specmatic 2.0.42__tar.gz → 2.29.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of specmatic might be problematic. Click here for more details.
- {specmatic-2.0.42/specmatic.egg-info → specmatic-2.29.0}/PKG-INFO +76 -65
- {specmatic-2.0.42 → specmatic-2.29.0}/README.md +64 -62
- {specmatic-2.0.42 → specmatic-2.29.0}/setup.py +2 -1
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/build_utils.py +2 -2
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/core/decorators.py +14 -14
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/core/specmatic.jar +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/core/specmatic.py +20 -20
- specmatic-2.0.42/specmatic/core/specmatic_stub.py → specmatic-2.29.0/specmatic/core/specmatic_mock.py +58 -31
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/generators/test_generator_base.py +1 -1
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/servers/asgi_app_server.py +2 -2
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/servers/wsgi_app_server.py +2 -2
- specmatic-2.29.0/specmatic/version.py +2 -0
- {specmatic-2.0.42 → specmatic-2.29.0/specmatic.egg-info}/PKG-INFO +76 -65
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic.egg-info/SOURCES.txt +1 -1
- specmatic-2.0.42/specmatic/version.py +0 -2
- {specmatic-2.0.42 → specmatic-2.29.0}/MANIFEST.in +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/setup.cfg +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/__init__.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/core/__init__.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/core/specmatic_base.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/core/specmatic_test.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/__init__.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/app_route_adapter.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/coverage_route.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/fastapi_app_route_adapter.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/flask_app_route_adapter.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/sanic_app_route_adapter.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/servers/__init__.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/servers/coverage_server.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/servers/fastapi_app_coverage_server.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/servers/flask_app_coverage_server.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/coverage/servers/sanic_app_coverage_server.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/generators/__init__.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/generators/pytest_generator.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/generators/unittest_generator.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/servers/__init__.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/servers/app_server.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/servers/wsgi_server_thread.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic/utils.py +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic.egg-info/dependency_links.txt +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic.egg-info/requires.txt +0 -0
- {specmatic-2.0.42 → specmatic-2.29.0}/specmatic.egg-info/top_level.txt +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: specmatic
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.29.0
|
|
4
4
|
Summary: A Python module for using the Specmatic Library.
|
|
5
|
-
Home-page: https://github.com/
|
|
5
|
+
Home-page: https://github.com/specmatic/specmatic-python-extensions
|
|
6
6
|
Author: Specmatic Builders
|
|
7
7
|
Author-email: info@core.in
|
|
8
|
+
Requires-Python: >=3.11
|
|
8
9
|
Description-Content-Type: text/markdown
|
|
9
10
|
Requires-Dist: pytest>=7.3.1
|
|
10
11
|
Requires-Dist: requests>=2.26.0
|
|
@@ -13,6 +14,14 @@ Requires-Dist: uvicorn>=0.18.0
|
|
|
13
14
|
Requires-Dist: fastapi>=0.70.0
|
|
14
15
|
Requires-Dist: flask>=2.2.5
|
|
15
16
|
Requires-Dist: sanic>=22.12.0
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: description
|
|
20
|
+
Dynamic: description-content-type
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-dist
|
|
23
|
+
Dynamic: requires-python
|
|
24
|
+
Dynamic: summary
|
|
16
25
|
|
|
17
26
|
# Specmatic Python
|
|
18
27
|
This is a Python library to run [Specmatic](https://specmatic.io).
|
|
@@ -23,7 +32,7 @@ Specmatic is a contract driven development tool that allows us to turn OpenAPI c
|
|
|
23
32
|
The specmatic python library provides three main functions:
|
|
24
33
|
- The ability to start and stop a python web app like flask/sanic.
|
|
25
34
|
- The ability to run specmatic in test mode against an open api contract/spec.
|
|
26
|
-
- The ability to
|
|
35
|
+
- The ability to mock out an api dependency using the specmatic mock feature.
|
|
27
36
|
|
|
28
37
|
#### Running Contract Tests
|
|
29
38
|
A contract test validates an open api specification against a running api service.
|
|
@@ -46,7 +55,7 @@ The open api specification can be present either locally or in a [Central Contra
|
|
|
46
55
|
|
|
47
56
|
#### How does it work
|
|
48
57
|
- Specmatic uses the TestContract class defined above to inject tests dynamically into it when you run it via PyTest or UnitTest.
|
|
49
|
-
- The Specmatic Python package, invokes the Specmatic executable jar (via command line) in a separate process to start
|
|
58
|
+
- The Specmatic Python package, invokes the Specmatic executable jar (via command line) in a separate process to start mocks and run tests.
|
|
50
59
|
- It is the specmatic jar which runs the contract tests and generates a JUnit test summary report.
|
|
51
60
|
- The Specmatic Python package ingests the JUnit test summary report and generates test methods corresponding to every contract test.
|
|
52
61
|
- These dynamic test methods are added to the ```TestContract``` class and hence we seem them reported seamlessly by PyTest/Unittest like this:
|
|
@@ -60,23 +69,22 @@ test/test_contract_with_coverage.py::TestContract::test_Scenario: GET /products
|
|
|
60
69
|
|
|
61
70
|
## WSGI Apps
|
|
62
71
|
|
|
63
|
-
#### To run contract tests with a
|
|
72
|
+
#### To run contract tests with a mock for a wsgi app (like Flask):
|
|
64
73
|
|
|
65
74
|
``````python
|
|
66
75
|
class TestContract:
|
|
67
76
|
pass
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Specmatic()
|
|
71
|
-
.with_project_root(PROJECT_ROOT)
|
|
72
|
-
.
|
|
73
|
-
.with_wsgi_app(app, app_host, app_port)
|
|
74
|
-
.test(TestContract)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
Specmatic()
|
|
80
|
+
.with_project_root(PROJECT_ROOT)
|
|
81
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
82
|
+
.with_wsgi_app(app, app_host, app_port)
|
|
83
|
+
.test(TestContract)
|
|
75
84
|
.run()
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
|
|
78
86
|
if __name__ == '__main__':
|
|
79
|
-
pytest.main()
|
|
87
|
+
pytest.main()
|
|
80
88
|
``````
|
|
81
89
|
|
|
82
90
|
- In this, we are passing:
|
|
@@ -84,16 +92,16 @@ pytest.main()
|
|
|
84
92
|
- app_host and app_port. If they are not specified, the app will be started on a random available port on 127.0.0.1.
|
|
85
93
|
- You would need a [specmatic config](https://specmatic.io/documentation/specmatic_json.html) file to be present in the root directory of your project.
|
|
86
94
|
- an empty test class.
|
|
87
|
-
-
|
|
88
|
-
The
|
|
89
|
-
If they are not supplied, the
|
|
90
|
-
[Click here](https://specmatic.io/documentation/service_virtualization_tutorial.html) to learn more about
|
|
95
|
+
- mock_host, mock_port, optional list of json files to set expectations on the mock.
|
|
96
|
+
The mock_host, mock_port will be used to run the specmatic mock server.
|
|
97
|
+
If they are not supplied, the mock will be started on a random available port on 127.0.0.1.
|
|
98
|
+
[Click here](https://specmatic.io/documentation/service_virtualization_tutorial.html) to learn more about mocking/service virtualization.
|
|
91
99
|
- You can run this test from either your IDE or command line by pointing pytest to your test folder:
|
|
92
100
|
``````pytest test -v -s``````
|
|
93
101
|
- NOTE: Please ensure that you set the '-v' and '-s' flags while running pytest as otherwise pytest may swallow up the console output.
|
|
94
102
|
|
|
95
103
|
|
|
96
|
-
#### To run contract tests without a
|
|
104
|
+
#### To run contract tests without a mock:
|
|
97
105
|
|
|
98
106
|
``````python
|
|
99
107
|
class TestContract:
|
|
@@ -108,33 +116,35 @@ Specmatic() \
|
|
|
108
116
|
|
|
109
117
|
## ASGI Apps
|
|
110
118
|
|
|
111
|
-
#### To run contract tests with a
|
|
119
|
+
#### To run contract tests with a mock for an asgi app (like sanic):
|
|
112
120
|
- If you are using an asgi app like sanic, fastapi, use the ``````with_asgi_app`````` function and pass it a string in the 'module:app' format.
|
|
121
|
+
|
|
113
122
|
``````python
|
|
114
123
|
class TestContract:
|
|
115
124
|
pass
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Specmatic()
|
|
119
|
-
.with_project_root(PROJECT_ROOT)
|
|
120
|
-
.
|
|
121
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
122
|
-
.test(TestContract)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
Specmatic()
|
|
128
|
+
.with_project_root(PROJECT_ROOT)
|
|
129
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
130
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
131
|
+
.test(TestContract)
|
|
123
132
|
.run()
|
|
124
133
|
``````
|
|
125
134
|
|
|
126
|
-
### Passing extra arguments to
|
|
135
|
+
### Passing extra arguments to mock/test
|
|
127
136
|
- To pass arguments like '--strict', '--testBaseUrl', pass them as a list to the 'args' parameter:
|
|
137
|
+
|
|
128
138
|
``````python
|
|
129
139
|
class TestContract:
|
|
130
140
|
pass
|
|
131
141
|
|
|
132
142
|
|
|
133
|
-
Specmatic()
|
|
134
|
-
.with_project_root(PROJECT_ROOT)
|
|
135
|
-
.
|
|
136
|
-
.with_wsgi_app(app, port=app_port)
|
|
137
|
-
.test(TestContract, args=['--testBaseURL=http://localhost:5000'])
|
|
143
|
+
Specmatic()
|
|
144
|
+
.with_project_root(PROJECT_ROOT)
|
|
145
|
+
.with_mock(mock_host, mock_port, [expectation_json_file], ['--strict'])
|
|
146
|
+
.with_wsgi_app(app, port=app_port)
|
|
147
|
+
.test(TestContract, args=['--testBaseURL=http://localhost:5000'])
|
|
138
148
|
.run()
|
|
139
149
|
``````
|
|
140
150
|
|
|
@@ -148,11 +158,11 @@ class TestContract:
|
|
|
148
158
|
pass
|
|
149
159
|
|
|
150
160
|
|
|
151
|
-
Specmatic()
|
|
152
|
-
.with_project_root(PROJECT_ROOT)
|
|
153
|
-
.
|
|
154
|
-
.with_wsgi_app(app, app_host, app_port)
|
|
155
|
-
.test_with_api_coverage_for_flask_app(TestContract, app)
|
|
161
|
+
Specmatic()
|
|
162
|
+
.with_project_root(PROJECT_ROOT)
|
|
163
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
164
|
+
.with_wsgi_app(app, app_host, app_port)
|
|
165
|
+
.test_with_api_coverage_for_flask_app(TestContract, app)
|
|
156
166
|
.run()
|
|
157
167
|
``````
|
|
158
168
|
|
|
@@ -163,11 +173,11 @@ class TestContract:
|
|
|
163
173
|
pass
|
|
164
174
|
|
|
165
175
|
|
|
166
|
-
Specmatic()
|
|
167
|
-
.with_project_root(PROJECT_ROOT)
|
|
168
|
-
.
|
|
169
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
170
|
-
.test_with_api_coverage_for_sanic_app(TestContract, app)
|
|
176
|
+
Specmatic()
|
|
177
|
+
.with_project_root(PROJECT_ROOT)
|
|
178
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
179
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
180
|
+
.test_with_api_coverage_for_sanic_app(TestContract, app)
|
|
171
181
|
.run()
|
|
172
182
|
``````
|
|
173
183
|
|
|
@@ -178,11 +188,11 @@ class TestContract:
|
|
|
178
188
|
pass
|
|
179
189
|
|
|
180
190
|
|
|
181
|
-
Specmatic()
|
|
182
|
-
.with_project_root(PROJECT_ROOT)
|
|
183
|
-
.
|
|
184
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
185
|
-
.test_with_api_coverage_for_fastapi_app(TestContract, app)
|
|
191
|
+
Specmatic()
|
|
192
|
+
.with_project_root(PROJECT_ROOT)
|
|
193
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
194
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
195
|
+
.test_with_api_coverage_for_fastapi_app(TestContract, app)
|
|
186
196
|
.run()
|
|
187
197
|
``````
|
|
188
198
|
|
|
@@ -193,14 +203,14 @@ The ``````CoverageRoute`````` class has two properties:
|
|
|
193
203
|
``````url`````` : This represents your route url in this format: `````` /orders/{order_id}``````
|
|
194
204
|
``````method`````` : A list of HTTP methods supported on the route, for instance : ``````['GET', 'POST']``````
|
|
195
205
|
|
|
196
|
-
You can then enable coverage by passing your adapter like this:
|
|
206
|
+
You can then enable coverage by passing your adapter like this:
|
|
197
207
|
|
|
198
208
|
``````python
|
|
199
|
-
Specmatic()
|
|
200
|
-
.with_project_root(PROJECT_ROOT)
|
|
201
|
-
.
|
|
202
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
203
|
-
.test_with_api_coverage(TestContract, MyAppRouteAdapter(app))
|
|
209
|
+
Specmatic()
|
|
210
|
+
.with_project_root(PROJECT_ROOT)
|
|
211
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
212
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
213
|
+
.test_with_api_coverage(TestContract, MyAppRouteAdapter(app))
|
|
204
214
|
.run()
|
|
205
215
|
``````
|
|
206
216
|
|
|
@@ -215,7 +225,8 @@ You can also easily implement your own coverage server if you have written a cus
|
|
|
215
225
|
The only point to remember in mind is that the EndPointsApi url should return a list of routes in the format used buy Spring Actuator's ```````/actuator/mappings``````` endpoint
|
|
216
226
|
as described [here](https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#mappings).
|
|
217
227
|
|
|
218
|
-
Here's an example where we start both our FastApi app and coverage server outside the specmatic api call.
|
|
228
|
+
Here's an example where we start both our FastApi app and coverage server outside the specmatic api call.
|
|
229
|
+
|
|
219
230
|
``````python
|
|
220
231
|
app_server = ASGIAppServer('test.apps.fast_api:app', app_host, app_port)
|
|
221
232
|
coverage_server = FastApiAppCoverageServer(app)
|
|
@@ -228,11 +239,11 @@ class TestContract:
|
|
|
228
239
|
pass
|
|
229
240
|
|
|
230
241
|
|
|
231
|
-
Specmatic()
|
|
232
|
-
.with_project_root(PROJECT_ROOT)
|
|
233
|
-
.
|
|
234
|
-
.with_endpoints_api(coverage_server.endpoints_api)
|
|
235
|
-
.test(TestContract, app_host, app_port)
|
|
242
|
+
Specmatic()
|
|
243
|
+
.with_project_root(PROJECT_ROOT)
|
|
244
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
245
|
+
.with_endpoints_api(coverage_server.endpoints_api)
|
|
246
|
+
.test(TestContract, app_host, app_port)
|
|
236
247
|
.run()
|
|
237
248
|
|
|
238
249
|
app_server.stop()
|
|
@@ -251,9 +262,9 @@ coverage_server.stop()
|
|
|
251
262
|
If you are able to get the app started using uvicorn, it will work with specmatic too.
|
|
252
263
|
|
|
253
264
|
## Sample Projects
|
|
254
|
-
- [Check out the Specmatic Order BFF Python repo](https://github.com/
|
|
255
|
-
- [Check out the Specmatic Order BFF Python Sanic repo](https://github.com/
|
|
256
|
-
- [Check out the Specmatic Order API Python repo](https://github.com/
|
|
265
|
+
- [Check out the Specmatic Order BFF Python repo](https://github.com/specmatic/specmatic-order-bff-python/) to see more examples of how to use specmatic with a Flask app.
|
|
266
|
+
- [Check out the Specmatic Order BFF Python Sanic repo](https://github.com/specmatic/specmatic-order-bff-python-sanic/) to see more examples of how to use specmatic with a Sanic app.
|
|
267
|
+
- [Check out the Specmatic Order API Python repo](https://github.com/specmatic/specmatic-order-api-python/) to see an examples of how to just run tests without using a mock.
|
|
257
268
|
|
|
258
269
|
|
|
259
270
|
|
|
@@ -7,7 +7,7 @@ Specmatic is a contract driven development tool that allows us to turn OpenAPI c
|
|
|
7
7
|
The specmatic python library provides three main functions:
|
|
8
8
|
- The ability to start and stop a python web app like flask/sanic.
|
|
9
9
|
- The ability to run specmatic in test mode against an open api contract/spec.
|
|
10
|
-
- The ability to
|
|
10
|
+
- The ability to mock out an api dependency using the specmatic mock feature.
|
|
11
11
|
|
|
12
12
|
#### Running Contract Tests
|
|
13
13
|
A contract test validates an open api specification against a running api service.
|
|
@@ -30,7 +30,7 @@ The open api specification can be present either locally or in a [Central Contra
|
|
|
30
30
|
|
|
31
31
|
#### How does it work
|
|
32
32
|
- Specmatic uses the TestContract class defined above to inject tests dynamically into it when you run it via PyTest or UnitTest.
|
|
33
|
-
- The Specmatic Python package, invokes the Specmatic executable jar (via command line) in a separate process to start
|
|
33
|
+
- The Specmatic Python package, invokes the Specmatic executable jar (via command line) in a separate process to start mocks and run tests.
|
|
34
34
|
- It is the specmatic jar which runs the contract tests and generates a JUnit test summary report.
|
|
35
35
|
- The Specmatic Python package ingests the JUnit test summary report and generates test methods corresponding to every contract test.
|
|
36
36
|
- These dynamic test methods are added to the ```TestContract``` class and hence we seem them reported seamlessly by PyTest/Unittest like this:
|
|
@@ -44,23 +44,22 @@ test/test_contract_with_coverage.py::TestContract::test_Scenario: GET /products
|
|
|
44
44
|
|
|
45
45
|
## WSGI Apps
|
|
46
46
|
|
|
47
|
-
#### To run contract tests with a
|
|
47
|
+
#### To run contract tests with a mock for a wsgi app (like Flask):
|
|
48
48
|
|
|
49
49
|
``````python
|
|
50
50
|
class TestContract:
|
|
51
51
|
pass
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Specmatic()
|
|
55
|
-
.with_project_root(PROJECT_ROOT)
|
|
56
|
-
.
|
|
57
|
-
.with_wsgi_app(app, app_host, app_port)
|
|
58
|
-
.test(TestContract)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
Specmatic()
|
|
55
|
+
.with_project_root(PROJECT_ROOT)
|
|
56
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
57
|
+
.with_wsgi_app(app, app_host, app_port)
|
|
58
|
+
.test(TestContract)
|
|
59
59
|
.run()
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
|
|
62
61
|
if __name__ == '__main__':
|
|
63
|
-
pytest.main()
|
|
62
|
+
pytest.main()
|
|
64
63
|
``````
|
|
65
64
|
|
|
66
65
|
- In this, we are passing:
|
|
@@ -68,16 +67,16 @@ pytest.main()
|
|
|
68
67
|
- app_host and app_port. If they are not specified, the app will be started on a random available port on 127.0.0.1.
|
|
69
68
|
- You would need a [specmatic config](https://specmatic.io/documentation/specmatic_json.html) file to be present in the root directory of your project.
|
|
70
69
|
- an empty test class.
|
|
71
|
-
-
|
|
72
|
-
The
|
|
73
|
-
If they are not supplied, the
|
|
74
|
-
[Click here](https://specmatic.io/documentation/service_virtualization_tutorial.html) to learn more about
|
|
70
|
+
- mock_host, mock_port, optional list of json files to set expectations on the mock.
|
|
71
|
+
The mock_host, mock_port will be used to run the specmatic mock server.
|
|
72
|
+
If they are not supplied, the mock will be started on a random available port on 127.0.0.1.
|
|
73
|
+
[Click here](https://specmatic.io/documentation/service_virtualization_tutorial.html) to learn more about mocking/service virtualization.
|
|
75
74
|
- You can run this test from either your IDE or command line by pointing pytest to your test folder:
|
|
76
75
|
``````pytest test -v -s``````
|
|
77
76
|
- NOTE: Please ensure that you set the '-v' and '-s' flags while running pytest as otherwise pytest may swallow up the console output.
|
|
78
77
|
|
|
79
78
|
|
|
80
|
-
#### To run contract tests without a
|
|
79
|
+
#### To run contract tests without a mock:
|
|
81
80
|
|
|
82
81
|
``````python
|
|
83
82
|
class TestContract:
|
|
@@ -92,33 +91,35 @@ Specmatic() \
|
|
|
92
91
|
|
|
93
92
|
## ASGI Apps
|
|
94
93
|
|
|
95
|
-
#### To run contract tests with a
|
|
94
|
+
#### To run contract tests with a mock for an asgi app (like sanic):
|
|
96
95
|
- If you are using an asgi app like sanic, fastapi, use the ``````with_asgi_app`````` function and pass it a string in the 'module:app' format.
|
|
96
|
+
|
|
97
97
|
``````python
|
|
98
98
|
class TestContract:
|
|
99
99
|
pass
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Specmatic()
|
|
103
|
-
.with_project_root(PROJECT_ROOT)
|
|
104
|
-
.
|
|
105
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
106
|
-
.test(TestContract)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Specmatic()
|
|
103
|
+
.with_project_root(PROJECT_ROOT)
|
|
104
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
105
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
106
|
+
.test(TestContract)
|
|
107
107
|
.run()
|
|
108
108
|
``````
|
|
109
109
|
|
|
110
|
-
### Passing extra arguments to
|
|
110
|
+
### Passing extra arguments to mock/test
|
|
111
111
|
- To pass arguments like '--strict', '--testBaseUrl', pass them as a list to the 'args' parameter:
|
|
112
|
+
|
|
112
113
|
``````python
|
|
113
114
|
class TestContract:
|
|
114
115
|
pass
|
|
115
116
|
|
|
116
117
|
|
|
117
|
-
Specmatic()
|
|
118
|
-
.with_project_root(PROJECT_ROOT)
|
|
119
|
-
.
|
|
120
|
-
.with_wsgi_app(app, port=app_port)
|
|
121
|
-
.test(TestContract, args=['--testBaseURL=http://localhost:5000'])
|
|
118
|
+
Specmatic()
|
|
119
|
+
.with_project_root(PROJECT_ROOT)
|
|
120
|
+
.with_mock(mock_host, mock_port, [expectation_json_file], ['--strict'])
|
|
121
|
+
.with_wsgi_app(app, port=app_port)
|
|
122
|
+
.test(TestContract, args=['--testBaseURL=http://localhost:5000'])
|
|
122
123
|
.run()
|
|
123
124
|
``````
|
|
124
125
|
|
|
@@ -132,11 +133,11 @@ class TestContract:
|
|
|
132
133
|
pass
|
|
133
134
|
|
|
134
135
|
|
|
135
|
-
Specmatic()
|
|
136
|
-
.with_project_root(PROJECT_ROOT)
|
|
137
|
-
.
|
|
138
|
-
.with_wsgi_app(app, app_host, app_port)
|
|
139
|
-
.test_with_api_coverage_for_flask_app(TestContract, app)
|
|
136
|
+
Specmatic()
|
|
137
|
+
.with_project_root(PROJECT_ROOT)
|
|
138
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
139
|
+
.with_wsgi_app(app, app_host, app_port)
|
|
140
|
+
.test_with_api_coverage_for_flask_app(TestContract, app)
|
|
140
141
|
.run()
|
|
141
142
|
``````
|
|
142
143
|
|
|
@@ -147,11 +148,11 @@ class TestContract:
|
|
|
147
148
|
pass
|
|
148
149
|
|
|
149
150
|
|
|
150
|
-
Specmatic()
|
|
151
|
-
.with_project_root(PROJECT_ROOT)
|
|
152
|
-
.
|
|
153
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
154
|
-
.test_with_api_coverage_for_sanic_app(TestContract, app)
|
|
151
|
+
Specmatic()
|
|
152
|
+
.with_project_root(PROJECT_ROOT)
|
|
153
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
154
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
155
|
+
.test_with_api_coverage_for_sanic_app(TestContract, app)
|
|
155
156
|
.run()
|
|
156
157
|
``````
|
|
157
158
|
|
|
@@ -162,11 +163,11 @@ class TestContract:
|
|
|
162
163
|
pass
|
|
163
164
|
|
|
164
165
|
|
|
165
|
-
Specmatic()
|
|
166
|
-
.with_project_root(PROJECT_ROOT)
|
|
167
|
-
.
|
|
168
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
169
|
-
.test_with_api_coverage_for_fastapi_app(TestContract, app)
|
|
166
|
+
Specmatic()
|
|
167
|
+
.with_project_root(PROJECT_ROOT)
|
|
168
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
169
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
170
|
+
.test_with_api_coverage_for_fastapi_app(TestContract, app)
|
|
170
171
|
.run()
|
|
171
172
|
``````
|
|
172
173
|
|
|
@@ -177,14 +178,14 @@ The ``````CoverageRoute`````` class has two properties:
|
|
|
177
178
|
``````url`````` : This represents your route url in this format: `````` /orders/{order_id}``````
|
|
178
179
|
``````method`````` : A list of HTTP methods supported on the route, for instance : ``````['GET', 'POST']``````
|
|
179
180
|
|
|
180
|
-
You can then enable coverage by passing your adapter like this:
|
|
181
|
+
You can then enable coverage by passing your adapter like this:
|
|
181
182
|
|
|
182
183
|
``````python
|
|
183
|
-
Specmatic()
|
|
184
|
-
.with_project_root(PROJECT_ROOT)
|
|
185
|
-
.
|
|
186
|
-
.with_asgi_app('main:app', app_host, app_port)
|
|
187
|
-
.test_with_api_coverage(TestContract, MyAppRouteAdapter(app))
|
|
184
|
+
Specmatic()
|
|
185
|
+
.with_project_root(PROJECT_ROOT)
|
|
186
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
187
|
+
.with_asgi_app('main:app', app_host, app_port)
|
|
188
|
+
.test_with_api_coverage(TestContract, MyAppRouteAdapter(app))
|
|
188
189
|
.run()
|
|
189
190
|
``````
|
|
190
191
|
|
|
@@ -199,7 +200,8 @@ You can also easily implement your own coverage server if you have written a cus
|
|
|
199
200
|
The only point to remember in mind is that the EndPointsApi url should return a list of routes in the format used buy Spring Actuator's ```````/actuator/mappings``````` endpoint
|
|
200
201
|
as described [here](https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#mappings).
|
|
201
202
|
|
|
202
|
-
Here's an example where we start both our FastApi app and coverage server outside the specmatic api call.
|
|
203
|
+
Here's an example where we start both our FastApi app and coverage server outside the specmatic api call.
|
|
204
|
+
|
|
203
205
|
``````python
|
|
204
206
|
app_server = ASGIAppServer('test.apps.fast_api:app', app_host, app_port)
|
|
205
207
|
coverage_server = FastApiAppCoverageServer(app)
|
|
@@ -212,11 +214,11 @@ class TestContract:
|
|
|
212
214
|
pass
|
|
213
215
|
|
|
214
216
|
|
|
215
|
-
Specmatic()
|
|
216
|
-
.with_project_root(PROJECT_ROOT)
|
|
217
|
-
.
|
|
218
|
-
.with_endpoints_api(coverage_server.endpoints_api)
|
|
219
|
-
.test(TestContract, app_host, app_port)
|
|
217
|
+
Specmatic()
|
|
218
|
+
.with_project_root(PROJECT_ROOT)
|
|
219
|
+
.with_mock(mock_host, mock_port, [expectation_json_file])
|
|
220
|
+
.with_endpoints_api(coverage_server.endpoints_api)
|
|
221
|
+
.test(TestContract, app_host, app_port)
|
|
220
222
|
.run()
|
|
221
223
|
|
|
222
224
|
app_server.stop()
|
|
@@ -235,9 +237,9 @@ coverage_server.stop()
|
|
|
235
237
|
If you are able to get the app started using uvicorn, it will work with specmatic too.
|
|
236
238
|
|
|
237
239
|
## Sample Projects
|
|
238
|
-
- [Check out the Specmatic Order BFF Python repo](https://github.com/
|
|
239
|
-
- [Check out the Specmatic Order BFF Python Sanic repo](https://github.com/
|
|
240
|
-
- [Check out the Specmatic Order API Python repo](https://github.com/
|
|
240
|
+
- [Check out the Specmatic Order BFF Python repo](https://github.com/specmatic/specmatic-order-bff-python/) to see more examples of how to use specmatic with a Flask app.
|
|
241
|
+
- [Check out the Specmatic Order BFF Python Sanic repo](https://github.com/specmatic/specmatic-order-bff-python-sanic/) to see more examples of how to use specmatic with a Sanic app.
|
|
242
|
+
- [Check out the Specmatic Order API Python repo](https://github.com/specmatic/specmatic-order-api-python/) to see an examples of how to just run tests without using a mock.
|
|
241
243
|
|
|
242
244
|
|
|
243
245
|
|
|
@@ -14,13 +14,14 @@ long_description = (this_directory / "README.md").read_text()
|
|
|
14
14
|
|
|
15
15
|
setup(
|
|
16
16
|
name='specmatic',
|
|
17
|
+
python_requires='>=3.11',
|
|
17
18
|
version=version['__version__'],
|
|
18
19
|
description='A Python module for using the Specmatic Library.',
|
|
19
20
|
long_description=long_description,
|
|
20
21
|
long_description_content_type='text/markdown',
|
|
21
22
|
author='Specmatic Builders',
|
|
22
23
|
author_email='info@core.in',
|
|
23
|
-
url='https://github.com/
|
|
24
|
+
url='https://github.com/specmatic/specmatic-python-extensions',
|
|
24
25
|
packages=find_packages(exclude=['test', 'test.*']),
|
|
25
26
|
include_package_data=True,
|
|
26
27
|
install_requires=[
|
|
@@ -4,8 +4,8 @@ from specmatic.utils import get_project_root
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def download_specmatic_jar(version):
|
|
7
|
-
file_url = f
|
|
8
|
-
file_path = get_project_root() +
|
|
7
|
+
file_url = f"https://github.com/specmatic/specmatic/releases/download/{version}/specmatic.jar"
|
|
8
|
+
file_path = get_project_root() + "/specmatic/core/specmatic.jar"
|
|
9
9
|
print(f"Downloading specmatic jar from: {file_url}")
|
|
10
10
|
urllib.request.urlretrieve(file_url, file_path)
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
|
|
3
|
-
from specmatic.core.
|
|
3
|
+
from specmatic.core.specmatic_mock import SpecmaticMock
|
|
4
4
|
from specmatic.core.specmatic_test import SpecmaticTest
|
|
5
5
|
from specmatic.coverage.app_route_adapter import AppRouteAdapter
|
|
6
6
|
from specmatic.coverage.servers.coverage_server import CoverageServer
|
|
@@ -11,15 +11,15 @@ from specmatic.servers.wsgi_app_server import WSGIAppServer
|
|
|
11
11
|
from specmatic.utils import get_junit_report_file_path
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def specmatic_mock(host: str = '127.0.0.1', port: int = 0, project_root: str = '', expectations=None,
|
|
15
15
|
specmatic_config_file_path: str = ''):
|
|
16
16
|
def decorator(cls):
|
|
17
17
|
try:
|
|
18
|
-
cls.
|
|
19
|
-
cls.
|
|
18
|
+
cls.mock = SpecmaticMock(host, port, project_root, specmatic_config_file_path)
|
|
19
|
+
cls.mock.set_expectations(expectations)
|
|
20
20
|
except Exception as e:
|
|
21
|
-
if hasattr(cls, '
|
|
22
|
-
cls.
|
|
21
|
+
if hasattr(cls, 'mock'):
|
|
22
|
+
cls.mock.stop()
|
|
23
23
|
if hasattr(cls, 'app'):
|
|
24
24
|
cls.app.stop()
|
|
25
25
|
print(f"Error: {e}")
|
|
@@ -60,15 +60,15 @@ def specmatic_contract_test(host: str = '127.0.0,1', port: int = 0,
|
|
|
60
60
|
except Exception as e:
|
|
61
61
|
if hasattr(cls, 'app'):
|
|
62
62
|
cls.app.stop()
|
|
63
|
-
if hasattr(cls, '
|
|
64
|
-
cls.
|
|
63
|
+
if hasattr(cls, 'mock'):
|
|
64
|
+
cls.mock.stop()
|
|
65
65
|
if hasattr(cls, 'coverage_server'):
|
|
66
66
|
cls.coverage_server.stop()
|
|
67
67
|
print(f"Error: {e}")
|
|
68
68
|
raise e
|
|
69
69
|
finally:
|
|
70
|
-
if hasattr(cls, '
|
|
71
|
-
cls.
|
|
70
|
+
if hasattr(cls, 'mock'):
|
|
71
|
+
cls.mock.stop()
|
|
72
72
|
if hasattr(cls, 'app'):
|
|
73
73
|
cls.app.stop()
|
|
74
74
|
if hasattr(cls, 'coverage_server'):
|
|
@@ -84,8 +84,8 @@ def start_wsgi_app(app, host: str = '127.0.0.1', port: int = 0):
|
|
|
84
84
|
cls.app.start()
|
|
85
85
|
return cls
|
|
86
86
|
except Exception as e:
|
|
87
|
-
if hasattr(cls, '
|
|
88
|
-
cls.
|
|
87
|
+
if hasattr(cls, 'mock'):
|
|
88
|
+
cls.mock.stop()
|
|
89
89
|
if hasattr(cls, 'app'):
|
|
90
90
|
cls.app.stop()
|
|
91
91
|
print(f"Error: {e}")
|
|
@@ -101,8 +101,8 @@ def start_asgi_app(app_module: str, host: str = '127.0.0.1', port: int = 0):
|
|
|
101
101
|
cls.app.start()
|
|
102
102
|
return cls
|
|
103
103
|
except Exception as e:
|
|
104
|
-
if hasattr(cls, '
|
|
105
|
-
cls.
|
|
104
|
+
if hasattr(cls, 'mock'):
|
|
105
|
+
cls.mock.stop()
|
|
106
106
|
if hasattr(cls, 'app'):
|
|
107
107
|
cls.app.stop()
|
|
108
108
|
print(f"Error: {e}")
|
|
Binary file
|