klab-pytest-toolkit-web 0.0.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.
- klab_pytest_toolkit_web-0.0.0/.gitignore +178 -0
- klab_pytest_toolkit_web-0.0.0/PKG-INFO +235 -0
- klab_pytest_toolkit_web-0.0.0/README.md +212 -0
- klab_pytest_toolkit_web-0.0.0/pyproject.toml +58 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/__init__.py +28 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/_api_client_types/__init__.py +22 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/_api_client_types/grpc_client.py +229 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/_api_client_types/rest_client.py +147 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/api_client.py +83 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/core.py +0 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/plugin.py +62 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/validators.py +88 -0
- klab_pytest_toolkit_web-0.0.0/src/klab_pytest_toolkit_web/web_client.py +360 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python
|
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
|
3
|
+
|
|
4
|
+
### Custom ###
|
|
5
|
+
pytest.xml
|
|
6
|
+
pytest-coverage.txt
|
|
7
|
+
tmp/
|
|
8
|
+
|
|
9
|
+
### Python ###
|
|
10
|
+
# Byte-compiled / optimized / DLL files
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
|
|
15
|
+
# Distribution / packaging
|
|
16
|
+
.Python
|
|
17
|
+
build/
|
|
18
|
+
develop-eggs/
|
|
19
|
+
dist/
|
|
20
|
+
downloads/
|
|
21
|
+
eggs/
|
|
22
|
+
.eggs/
|
|
23
|
+
lib/
|
|
24
|
+
lib64/
|
|
25
|
+
parts/
|
|
26
|
+
sdist/
|
|
27
|
+
var/
|
|
28
|
+
wheels/
|
|
29
|
+
share/python-wheels/
|
|
30
|
+
*.egg-info/
|
|
31
|
+
.installed.cfg
|
|
32
|
+
*.egg
|
|
33
|
+
MANIFEST
|
|
34
|
+
|
|
35
|
+
# PyInstaller
|
|
36
|
+
# Usually these files are written by a python script from a template
|
|
37
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
38
|
+
*.manifest
|
|
39
|
+
*.spec
|
|
40
|
+
|
|
41
|
+
# Installer logs
|
|
42
|
+
pip-log.txt
|
|
43
|
+
pip-delete-this-directory.txt
|
|
44
|
+
|
|
45
|
+
# Unit test / coverage reports
|
|
46
|
+
htmlcov/
|
|
47
|
+
.tox/
|
|
48
|
+
.nox/
|
|
49
|
+
.coverage
|
|
50
|
+
.coverage.*
|
|
51
|
+
.cache
|
|
52
|
+
nosetests.xml
|
|
53
|
+
coverage.xml
|
|
54
|
+
*.cover
|
|
55
|
+
*.py,cover
|
|
56
|
+
.hypothesis/
|
|
57
|
+
.pytest_cache/
|
|
58
|
+
cover/
|
|
59
|
+
|
|
60
|
+
# Translations
|
|
61
|
+
*.mo
|
|
62
|
+
*.pot
|
|
63
|
+
|
|
64
|
+
# Django stuff:
|
|
65
|
+
*.log
|
|
66
|
+
local_settings.py
|
|
67
|
+
db.sqlite3
|
|
68
|
+
db.sqlite3-journal
|
|
69
|
+
|
|
70
|
+
# Flask stuff:
|
|
71
|
+
instance/
|
|
72
|
+
.webassets-cache
|
|
73
|
+
|
|
74
|
+
# Scrapy stuff:
|
|
75
|
+
.scrapy
|
|
76
|
+
|
|
77
|
+
# Sphinx documentation
|
|
78
|
+
docs/_build/
|
|
79
|
+
|
|
80
|
+
# PyBuilder
|
|
81
|
+
.pybuilder/
|
|
82
|
+
target/
|
|
83
|
+
|
|
84
|
+
# Jupyter Notebook
|
|
85
|
+
.ipynb_checkpoints
|
|
86
|
+
|
|
87
|
+
# IPython
|
|
88
|
+
profile_default/
|
|
89
|
+
ipython_config.py
|
|
90
|
+
|
|
91
|
+
# pyenv
|
|
92
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
93
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
94
|
+
# .python-version
|
|
95
|
+
|
|
96
|
+
# pipenv
|
|
97
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
98
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
99
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
100
|
+
# install all needed dependencies.
|
|
101
|
+
#Pipfile.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
|
|
110
|
+
# pdm
|
|
111
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
112
|
+
#pdm.lock
|
|
113
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
114
|
+
# in version control.
|
|
115
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
116
|
+
.pdm.toml
|
|
117
|
+
|
|
118
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
119
|
+
__pypackages__/
|
|
120
|
+
|
|
121
|
+
# Celery stuff
|
|
122
|
+
celerybeat-schedule
|
|
123
|
+
celerybeat.pid
|
|
124
|
+
|
|
125
|
+
# SageMath parsed files
|
|
126
|
+
*.sage.py
|
|
127
|
+
|
|
128
|
+
# Environments
|
|
129
|
+
.env
|
|
130
|
+
.venv
|
|
131
|
+
env/
|
|
132
|
+
venv/
|
|
133
|
+
ENV/
|
|
134
|
+
env.bak/
|
|
135
|
+
venv.bak/
|
|
136
|
+
|
|
137
|
+
# Spyder project settings
|
|
138
|
+
.spyderproject
|
|
139
|
+
.spyproject
|
|
140
|
+
|
|
141
|
+
# Rope project settings
|
|
142
|
+
.ropeproject
|
|
143
|
+
|
|
144
|
+
# mkdocs documentation
|
|
145
|
+
/site
|
|
146
|
+
|
|
147
|
+
# mypy
|
|
148
|
+
.mypy_cache/
|
|
149
|
+
.dmypy.json
|
|
150
|
+
dmypy.json
|
|
151
|
+
|
|
152
|
+
# Pyre type checker
|
|
153
|
+
.pyre/
|
|
154
|
+
|
|
155
|
+
# pytype static type analyzer
|
|
156
|
+
.pytype/
|
|
157
|
+
|
|
158
|
+
# Cython debug symbols
|
|
159
|
+
cython_debug/
|
|
160
|
+
|
|
161
|
+
# PyCharm
|
|
162
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
163
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
164
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
165
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
166
|
+
#.idea/
|
|
167
|
+
|
|
168
|
+
### Python Patch ###
|
|
169
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
170
|
+
poetry.toml
|
|
171
|
+
|
|
172
|
+
# ruff
|
|
173
|
+
.ruff_cache/
|
|
174
|
+
|
|
175
|
+
# LSP config files
|
|
176
|
+
pyrightconfig.json
|
|
177
|
+
|
|
178
|
+
# End of https://www.toptal.com/developers/gitignore/api/python
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: klab-pytest-toolkit-web
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Pytest web fixtures for the Klab Pytest Toolkit
|
|
5
|
+
Project-URL: Changelog, https://github.com/klab365/klab-pytest-toolkit/blob/main/CHANGELOG.md
|
|
6
|
+
Project-URL: Repository, https://github.com/klab365/klab-pytest-toolkit
|
|
7
|
+
Project-URL: Issues, https://github.com/klab365/klab-pytest-toolkit/issues
|
|
8
|
+
Author-email: Burak Kizilkaya <burak.kizilkaya@outlook.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: <4,>=3.11
|
|
15
|
+
Requires-Dist: grpcio-reflection>=1.66.0
|
|
16
|
+
Requires-Dist: grpcio-tools>=1.66.0
|
|
17
|
+
Requires-Dist: grpcio>=1.66.0
|
|
18
|
+
Requires-Dist: jsonschema>=4.25.1
|
|
19
|
+
Requires-Dist: playwright>=1.57.0
|
|
20
|
+
Requires-Dist: pytest>=8.3.5
|
|
21
|
+
Requires-Dist: requests>=2.32.5
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Klab Pytest Toolkit - Web Fixtures
|
|
25
|
+
|
|
26
|
+
Custom pytest fixtures for web testing.
|
|
27
|
+
The goal is to allow testers to easily test web applications (html/json/rest api) with reusable components.
|
|
28
|
+
|
|
29
|
+
At the moment the package provides the following fixtures:
|
|
30
|
+
|
|
31
|
+
- `response_validator_factory`: Factory for create JSON response validator instances with custom configurations.
|
|
32
|
+
- `api_client_factory`: Factory for create different API client instances.
|
|
33
|
+
- REST API client for making HTTP requests to RESTful services.
|
|
34
|
+
- `web_client_factory`: Factory to create web client instances for browser automation
|
|
35
|
+
- Playwright-based web client for end-to-end testing of web applications.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install klab-pytest-toolkit-web
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### JSON Response Validator
|
|
46
|
+
|
|
47
|
+
**Create the fixture**
|
|
48
|
+
|
|
49
|
+
The factory class `ResponseValidatorFactory` is already provided as a pytest fixture `response_validator_factory`.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
@pytest.fixture
|
|
53
|
+
def json_validator_user_schema(response_validator_factory) -> JsonResponseValidator:
|
|
54
|
+
"""Fixture to provide a JSON response validator for user schema."""
|
|
55
|
+
user_schema = {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"id": {"type": "integer"},
|
|
59
|
+
"name": {"type": "string"},
|
|
60
|
+
},
|
|
61
|
+
"required": ["id", "name"]
|
|
62
|
+
}
|
|
63
|
+
return response_validator_factory.create_json_validator(schema=user_schema)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Functions**
|
|
67
|
+
|
|
68
|
+
The validator contains one main function to validate a response against the schema. Below is an example of how to use the validator in a test.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
def test_user_api(json_validator_user_schema):
|
|
72
|
+
"""Test user API response validation."""
|
|
73
|
+
response_data = {
|
|
74
|
+
"id": 1,
|
|
75
|
+
"name": "John Doe"
|
|
76
|
+
}
|
|
77
|
+
assert json_validator_user_schema.validate_response(response_data)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### REST API Client
|
|
81
|
+
|
|
82
|
+
**Create the fixture**
|
|
83
|
+
|
|
84
|
+
The factory class `ApiClientFactory` is already provided as a pytest fixture `api_client_factory`.
|
|
85
|
+
To pass the url or other header information, you can pass this as environment variables or configure directly in the fixture.
|
|
86
|
+
Below is an example of creating a REST API client fixture from a testcontainer url.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
|
|
90
|
+
@pytest.fixture(scope="session")
|
|
91
|
+
def httpbin_container():
|
|
92
|
+
"""Fixture to provide an HTTPBin container for testing."""
|
|
93
|
+
|
|
94
|
+
with DockerContainer("kennethreitz/httpbin:latest") as httpbin:
|
|
95
|
+
httpbin.with_exposed_ports(80)
|
|
96
|
+
httpbin.waiting_for(HttpWaitStrategy(path="/get", port=80).for_status_code(200))
|
|
97
|
+
httpbin.start()
|
|
98
|
+
port = httpbin.get_exposed_port(80)
|
|
99
|
+
base_url = f"http://localhost:{port}"
|
|
100
|
+
yield base_url
|
|
101
|
+
|
|
102
|
+
@pytest.fixture
|
|
103
|
+
def rest_api_client(api_client_factory, httpbin_container) -> RestApiClient:
|
|
104
|
+
"""Fixture to provide a REST API client."""
|
|
105
|
+
return api_client_factory.create_rest_client(base_url=httpbin_container)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Functions**
|
|
109
|
+
|
|
110
|
+
The REST API client provides functions to make HTTP requests. These are some examples:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
def test_get_request(rest_api_client: RestApiClient):
|
|
114
|
+
"""Test basic GET request with query parameters."""
|
|
115
|
+
response = rest_api_client.get("/get", params={"test": "value"})
|
|
116
|
+
|
|
117
|
+
assert response.status_code == 200
|
|
118
|
+
json_data = response.json()
|
|
119
|
+
assert json_data["args"]["test"] == "value"
|
|
120
|
+
|
|
121
|
+
def test_post_request(rest_api_client: RestApiClient):
|
|
122
|
+
"""Test basic POST request with JSON body."""
|
|
123
|
+
response = rest_api_client.post("/post", payload={"key": "value"})
|
|
124
|
+
|
|
125
|
+
assert response.status_code == 200
|
|
126
|
+
json_data = response.json()
|
|
127
|
+
assert json_data["json"]["key"] == "value"
|
|
128
|
+
|
|
129
|
+
def test_update_request(rest_api_client: RestApiClient):
|
|
130
|
+
"""Test basic PUT request with JSON body."""
|
|
131
|
+
response = rest_api_client.put("/put", payload={"update": "data"})
|
|
132
|
+
|
|
133
|
+
assert response.status_code == 200
|
|
134
|
+
json_data = response.json()
|
|
135
|
+
assert json_data["json"]["update"] == "data"
|
|
136
|
+
|
|
137
|
+
def test_delete_request(rest_api_client: RestApiClient):
|
|
138
|
+
"""Test basic DELETE request."""
|
|
139
|
+
response = rest_api_client.delete("/delete")
|
|
140
|
+
|
|
141
|
+
assert response.status_code == 200
|
|
142
|
+
json_data = response.json()
|
|
143
|
+
assert json_data["url"].endswith("/delete")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### gRPC Client
|
|
147
|
+
|
|
148
|
+
**Create the fixture**
|
|
149
|
+
|
|
150
|
+
The factory class `ApiClientFactory` is already provided as a pytest fixture `api_client_factory`.
|
|
151
|
+
You can create a gRPC client fixture as shown below:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
@pytest.fixture
|
|
155
|
+
def grpc_client(api_client_factory) -> GrpcClient:
|
|
156
|
+
"""Fixture to provide a gRPC client."""
|
|
157
|
+
return api_client_factory.create_grpc_client(
|
|
158
|
+
target="localhost:50051",
|
|
159
|
+
proto_file="path/to/your/service.proto"
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Functions**
|
|
164
|
+
|
|
165
|
+
The `GrpcClient` provides gRPC call functionality by dynamically invoking methods defined in the provided proto file. It is imported the that the proto file defines a service with methods like `GetUser`. You will not get any code completion in your IDE since the methods are dynamically resolved at runtime, but you get error handling if the method does not exist.
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
def test_grpc_get_user(grpc_client: GrpcClient):
|
|
169
|
+
"""Test gRPC GetUser call."""
|
|
170
|
+
response = grpc_client.GetUser(id=123)
|
|
171
|
+
|
|
172
|
+
assert response.id == 123
|
|
173
|
+
assert response.name == "John Doe"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Playwright Web Client
|
|
177
|
+
|
|
178
|
+
**Create the fixture**
|
|
179
|
+
|
|
180
|
+
The factory class `WebClientFactory` is already provided as a pytest fixture `web_client_factory`.
|
|
181
|
+
For playwright, you might install the browsers first by running `playwright install` in your environment.
|
|
182
|
+
You can create a Playwright web client fixture as shown below:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
@pytest.fixture
|
|
186
|
+
def web_client(web_client_factory) -> PlaywrightWebClient:
|
|
187
|
+
"""Fixture to provide a Playwright web client."""
|
|
188
|
+
with web_client_factory.create_client(client_type="playwright", headless=True) as client:
|
|
189
|
+
yield client
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Functions**
|
|
193
|
+
|
|
194
|
+
The `WebClient` provides a variety of functions for browser automation. Refer to the api of the instance.
|
|
195
|
+
Here are some examples of common operations:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
def test_navigate_and_click(web_client):
|
|
199
|
+
"""Test navigation and clicking a button."""
|
|
200
|
+
web_client.navigate_to("https://example.com")
|
|
201
|
+
web_client.click("#start-button")
|
|
202
|
+
assert web_client.get_text("#result") == "Started"
|
|
203
|
+
|
|
204
|
+
def test_form_submission(web_client):
|
|
205
|
+
"""Test form submission."""
|
|
206
|
+
web_client.navigate_to("https://example.com/form")
|
|
207
|
+
web_client.fill_input("#name", "Test User")
|
|
208
|
+
web_client.fill_input("#email", "max@muster.com")
|
|
209
|
+
web_client.click("#submit-button")
|
|
210
|
+
assert web_client.get_text("#confirmation") == "Thank you for your submission!"
|
|
211
|
+
|
|
212
|
+
def test_wait_for_element(web_client):
|
|
213
|
+
"""Test waiting for an element to appear."""
|
|
214
|
+
web_client.navigate_to("https://example.com/dynamic")
|
|
215
|
+
web_client.wait_for_element("#dynamic-content", timeout=10)
|
|
216
|
+
assert web_client.get_text("#dynamic-content") == "Loaded Content"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Examples
|
|
220
|
+
|
|
221
|
+
See the test files for comprehensive examples:
|
|
222
|
+
- `tests/test_jsonvalidator.py` - JSON validation examples covering basic validation, type checking, nested objects, constraints, and error handling
|
|
223
|
+
- `tests/test_restapiclient.py` - REST API client examples with testcontainers integration
|
|
224
|
+
- `tests/test_playwrightclient.py` - Playwright web client examples for browser automation with testcontainers
|
|
225
|
+
|
|
226
|
+
## Best Practices
|
|
227
|
+
|
|
228
|
+
### Use Testcontainers for Isolated Environments
|
|
229
|
+
|
|
230
|
+
When testing web applications, it's recommended to use testcontainers to create isolated environments for your services.
|
|
231
|
+
This ensures that your tests are reproducible and do not interfere with each other.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Klab Pytest Toolkit - Web Fixtures
|
|
2
|
+
|
|
3
|
+
Custom pytest fixtures for web testing.
|
|
4
|
+
The goal is to allow testers to easily test web applications (html/json/rest api) with reusable components.
|
|
5
|
+
|
|
6
|
+
At the moment the package provides the following fixtures:
|
|
7
|
+
|
|
8
|
+
- `response_validator_factory`: Factory for create JSON response validator instances with custom configurations.
|
|
9
|
+
- `api_client_factory`: Factory for create different API client instances.
|
|
10
|
+
- REST API client for making HTTP requests to RESTful services.
|
|
11
|
+
- `web_client_factory`: Factory to create web client instances for browser automation
|
|
12
|
+
- Playwright-based web client for end-to-end testing of web applications.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install klab-pytest-toolkit-web
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### JSON Response Validator
|
|
23
|
+
|
|
24
|
+
**Create the fixture**
|
|
25
|
+
|
|
26
|
+
The factory class `ResponseValidatorFactory` is already provided as a pytest fixture `response_validator_factory`.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def json_validator_user_schema(response_validator_factory) -> JsonResponseValidator:
|
|
31
|
+
"""Fixture to provide a JSON response validator for user schema."""
|
|
32
|
+
user_schema = {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"properties": {
|
|
35
|
+
"id": {"type": "integer"},
|
|
36
|
+
"name": {"type": "string"},
|
|
37
|
+
},
|
|
38
|
+
"required": ["id", "name"]
|
|
39
|
+
}
|
|
40
|
+
return response_validator_factory.create_json_validator(schema=user_schema)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Functions**
|
|
44
|
+
|
|
45
|
+
The validator contains one main function to validate a response against the schema. Below is an example of how to use the validator in a test.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
def test_user_api(json_validator_user_schema):
|
|
49
|
+
"""Test user API response validation."""
|
|
50
|
+
response_data = {
|
|
51
|
+
"id": 1,
|
|
52
|
+
"name": "John Doe"
|
|
53
|
+
}
|
|
54
|
+
assert json_validator_user_schema.validate_response(response_data)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### REST API Client
|
|
58
|
+
|
|
59
|
+
**Create the fixture**
|
|
60
|
+
|
|
61
|
+
The factory class `ApiClientFactory` is already provided as a pytest fixture `api_client_factory`.
|
|
62
|
+
To pass the url or other header information, you can pass this as environment variables or configure directly in the fixture.
|
|
63
|
+
Below is an example of creating a REST API client fixture from a testcontainer url.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
|
|
67
|
+
@pytest.fixture(scope="session")
|
|
68
|
+
def httpbin_container():
|
|
69
|
+
"""Fixture to provide an HTTPBin container for testing."""
|
|
70
|
+
|
|
71
|
+
with DockerContainer("kennethreitz/httpbin:latest") as httpbin:
|
|
72
|
+
httpbin.with_exposed_ports(80)
|
|
73
|
+
httpbin.waiting_for(HttpWaitStrategy(path="/get", port=80).for_status_code(200))
|
|
74
|
+
httpbin.start()
|
|
75
|
+
port = httpbin.get_exposed_port(80)
|
|
76
|
+
base_url = f"http://localhost:{port}"
|
|
77
|
+
yield base_url
|
|
78
|
+
|
|
79
|
+
@pytest.fixture
|
|
80
|
+
def rest_api_client(api_client_factory, httpbin_container) -> RestApiClient:
|
|
81
|
+
"""Fixture to provide a REST API client."""
|
|
82
|
+
return api_client_factory.create_rest_client(base_url=httpbin_container)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Functions**
|
|
86
|
+
|
|
87
|
+
The REST API client provides functions to make HTTP requests. These are some examples:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
def test_get_request(rest_api_client: RestApiClient):
|
|
91
|
+
"""Test basic GET request with query parameters."""
|
|
92
|
+
response = rest_api_client.get("/get", params={"test": "value"})
|
|
93
|
+
|
|
94
|
+
assert response.status_code == 200
|
|
95
|
+
json_data = response.json()
|
|
96
|
+
assert json_data["args"]["test"] == "value"
|
|
97
|
+
|
|
98
|
+
def test_post_request(rest_api_client: RestApiClient):
|
|
99
|
+
"""Test basic POST request with JSON body."""
|
|
100
|
+
response = rest_api_client.post("/post", payload={"key": "value"})
|
|
101
|
+
|
|
102
|
+
assert response.status_code == 200
|
|
103
|
+
json_data = response.json()
|
|
104
|
+
assert json_data["json"]["key"] == "value"
|
|
105
|
+
|
|
106
|
+
def test_update_request(rest_api_client: RestApiClient):
|
|
107
|
+
"""Test basic PUT request with JSON body."""
|
|
108
|
+
response = rest_api_client.put("/put", payload={"update": "data"})
|
|
109
|
+
|
|
110
|
+
assert response.status_code == 200
|
|
111
|
+
json_data = response.json()
|
|
112
|
+
assert json_data["json"]["update"] == "data"
|
|
113
|
+
|
|
114
|
+
def test_delete_request(rest_api_client: RestApiClient):
|
|
115
|
+
"""Test basic DELETE request."""
|
|
116
|
+
response = rest_api_client.delete("/delete")
|
|
117
|
+
|
|
118
|
+
assert response.status_code == 200
|
|
119
|
+
json_data = response.json()
|
|
120
|
+
assert json_data["url"].endswith("/delete")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### gRPC Client
|
|
124
|
+
|
|
125
|
+
**Create the fixture**
|
|
126
|
+
|
|
127
|
+
The factory class `ApiClientFactory` is already provided as a pytest fixture `api_client_factory`.
|
|
128
|
+
You can create a gRPC client fixture as shown below:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
@pytest.fixture
|
|
132
|
+
def grpc_client(api_client_factory) -> GrpcClient:
|
|
133
|
+
"""Fixture to provide a gRPC client."""
|
|
134
|
+
return api_client_factory.create_grpc_client(
|
|
135
|
+
target="localhost:50051",
|
|
136
|
+
proto_file="path/to/your/service.proto"
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Functions**
|
|
141
|
+
|
|
142
|
+
The `GrpcClient` provides gRPC call functionality by dynamically invoking methods defined in the provided proto file. It is imported the that the proto file defines a service with methods like `GetUser`. You will not get any code completion in your IDE since the methods are dynamically resolved at runtime, but you get error handling if the method does not exist.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
def test_grpc_get_user(grpc_client: GrpcClient):
|
|
146
|
+
"""Test gRPC GetUser call."""
|
|
147
|
+
response = grpc_client.GetUser(id=123)
|
|
148
|
+
|
|
149
|
+
assert response.id == 123
|
|
150
|
+
assert response.name == "John Doe"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Playwright Web Client
|
|
154
|
+
|
|
155
|
+
**Create the fixture**
|
|
156
|
+
|
|
157
|
+
The factory class `WebClientFactory` is already provided as a pytest fixture `web_client_factory`.
|
|
158
|
+
For playwright, you might install the browsers first by running `playwright install` in your environment.
|
|
159
|
+
You can create a Playwright web client fixture as shown below:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
@pytest.fixture
|
|
163
|
+
def web_client(web_client_factory) -> PlaywrightWebClient:
|
|
164
|
+
"""Fixture to provide a Playwright web client."""
|
|
165
|
+
with web_client_factory.create_client(client_type="playwright", headless=True) as client:
|
|
166
|
+
yield client
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Functions**
|
|
170
|
+
|
|
171
|
+
The `WebClient` provides a variety of functions for browser automation. Refer to the api of the instance.
|
|
172
|
+
Here are some examples of common operations:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
def test_navigate_and_click(web_client):
|
|
176
|
+
"""Test navigation and clicking a button."""
|
|
177
|
+
web_client.navigate_to("https://example.com")
|
|
178
|
+
web_client.click("#start-button")
|
|
179
|
+
assert web_client.get_text("#result") == "Started"
|
|
180
|
+
|
|
181
|
+
def test_form_submission(web_client):
|
|
182
|
+
"""Test form submission."""
|
|
183
|
+
web_client.navigate_to("https://example.com/form")
|
|
184
|
+
web_client.fill_input("#name", "Test User")
|
|
185
|
+
web_client.fill_input("#email", "max@muster.com")
|
|
186
|
+
web_client.click("#submit-button")
|
|
187
|
+
assert web_client.get_text("#confirmation") == "Thank you for your submission!"
|
|
188
|
+
|
|
189
|
+
def test_wait_for_element(web_client):
|
|
190
|
+
"""Test waiting for an element to appear."""
|
|
191
|
+
web_client.navigate_to("https://example.com/dynamic")
|
|
192
|
+
web_client.wait_for_element("#dynamic-content", timeout=10)
|
|
193
|
+
assert web_client.get_text("#dynamic-content") == "Loaded Content"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Examples
|
|
197
|
+
|
|
198
|
+
See the test files for comprehensive examples:
|
|
199
|
+
- `tests/test_jsonvalidator.py` - JSON validation examples covering basic validation, type checking, nested objects, constraints, and error handling
|
|
200
|
+
- `tests/test_restapiclient.py` - REST API client examples with testcontainers integration
|
|
201
|
+
- `tests/test_playwrightclient.py` - Playwright web client examples for browser automation with testcontainers
|
|
202
|
+
|
|
203
|
+
## Best Practices
|
|
204
|
+
|
|
205
|
+
### Use Testcontainers for Isolated Environments
|
|
206
|
+
|
|
207
|
+
When testing web applications, it's recommended to use testcontainers to create isolated environments for your services.
|
|
208
|
+
This ensures that your tests are reproducible and do not interfere with each other.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "klab-pytest-toolkit-web"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Pytest web fixtures for the Klab Pytest Toolkit"
|
|
9
|
+
authors = [{ name = "Burak Kizilkaya", email = "burak.kizilkaya@outlook.com" }]
|
|
10
|
+
requires-python = ">=3.11,<4"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"jsonschema>=4.25.1",
|
|
21
|
+
"pytest>=8.3.5",
|
|
22
|
+
"playwright>=1.57.0",
|
|
23
|
+
"requests>=2.32.5",
|
|
24
|
+
"grpcio>=1.66.0",
|
|
25
|
+
"grpcio-tools>=1.66.0",
|
|
26
|
+
"grpcio-reflection>=1.66.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Changelog = "https://github.com/klab365/klab-pytest-toolkit/blob/main/CHANGELOG.md"
|
|
31
|
+
Repository = "https://github.com/klab365/klab-pytest-toolkit"
|
|
32
|
+
Issues = "https://github.com/klab365/klab-pytest-toolkit/issues"
|
|
33
|
+
|
|
34
|
+
[project.entry-points.pytest11]
|
|
35
|
+
klab_pytest_toolkit_web = "klab_pytest_toolkit_web.plugin"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.version]
|
|
38
|
+
path = "src/klab_pytest_toolkit_web/__init__.py"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.sdist]
|
|
41
|
+
exclude = [
|
|
42
|
+
"tests",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
asyncio_mode = "auto"
|
|
47
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
48
|
+
pythonpath = [
|
|
49
|
+
"."
|
|
50
|
+
]
|
|
51
|
+
filterwarnings = [
|
|
52
|
+
"ignore::DeprecationWarning:testcontainers.core.waiting_utils",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[dependency-groups]
|
|
56
|
+
dev = [
|
|
57
|
+
"testcontainers>=4.13.3",
|
|
58
|
+
]
|