testit-adapter-nose 3.10.8.post550__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.
- testit_adapter_nose-3.10.8.post550/PKG-INFO +223 -0
- testit_adapter_nose-3.10.8.post550/README.md +193 -0
- testit_adapter_nose-3.10.8.post550/setup.cfg +4 -0
- testit_adapter_nose-3.10.8.post550/setup.py +34 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose/__init__.py +0 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose/listener.py +69 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose/plugin.py +43 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose/utils.py +380 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose.egg-info/PKG-INFO +223 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose.egg-info/SOURCES.txt +13 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose.egg-info/dependency_links.txt +1 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose.egg-info/entry_points.txt +2 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose.egg-info/requires.txt +3 -0
- testit_adapter_nose-3.10.8.post550/src/testit_adapter_nose.egg-info/top_level.txt +1 -0
- testit_adapter_nose-3.10.8.post550/tests/test_plugin.py +24 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testit-adapter-nose
|
|
3
|
+
Version: 3.10.8.post550
|
|
4
|
+
Summary: Nose adapter for Test IT
|
|
5
|
+
Home-page: https://github.com/testit-tms/adapters-python/
|
|
6
|
+
Author: Integration team
|
|
7
|
+
Author-email: integrations@testit.software
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: attrs
|
|
19
|
+
Requires-Dist: nose2
|
|
20
|
+
Requires-Dist: testit-python-commons==3.10.8.post550
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# Test IT TMS adapter for Nose
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
[](https://pypi.python.org/pypi/testit-adapter-nose)
|
|
37
|
+
[](https://pypi.python.org/pypi/testit-adapter-nose)
|
|
38
|
+
[](https://github.com/testit-tms/adapters-python)
|
|
39
|
+
|
|
40
|
+
## Getting Started
|
|
41
|
+
|
|
42
|
+
### Installation
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pip install testit-adapter-nose
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Configuration
|
|
51
|
+
|
|
52
|
+
| Description | File property | Environment variable |
|
|
53
|
+
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------|
|
|
54
|
+
| Location of the TMS instance | url | TMS_URL |
|
|
55
|
+
| API secret key [How to getting API secret key?](https://github.com/testit-tms/.github/tree/main/configuration#privatetoken) | privateToken | TMS_PRIVATE_TOKEN |
|
|
56
|
+
| ID of project in TMS instance [How to getting project ID?](https://github.com/testit-tms/.github/tree/main/configuration#projectid) | projectId | TMS_PROJECT_ID |
|
|
57
|
+
| ID of configuration in TMS instance [How to getting configuration ID?](https://github.com/testit-tms/.github/tree/main/configuration#configurationid) | configurationId | TMS_CONFIGURATION_ID |
|
|
58
|
+
| ID of the created test run in TMS instance.<br/>It's necessary for **adapterMode** 1 | testRunId | TMS_TEST_RUN_ID |
|
|
59
|
+
| Parameter for specifying the name of test run in TMS instance (**It's optional**). If it is not provided, it is created automatically | testRunName | TMS_TEST_RUN_NAME |
|
|
60
|
+
| Adapter mode. Default value - 1. The adapter supports following modes:<br>1 - in this mode, the adapter sends all results to the test run without filtering or [with filtering CLI](#run-with-filter)<br/>2 - in this mode, the adapter creates a new test run and sends results to the new test run | adapterMode | TMS_ADAPTER_MODE |
|
|
61
|
+
| It enables/disables certificate validation (**It's optional**). Default value - true | certValidation | TMS_CERT_VALIDATION |
|
|
62
|
+
| Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)<br/>false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES |
|
|
63
|
+
| Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will update links to test cases<br/>false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES |
|
|
64
|
+
| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:<br/>true - in this mode, the adapter will create/update each autotest in real time<br/>false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME |
|
|
65
|
+
| Url of proxy server (**It's optional**) | tmsProxy | TMS_PROXY |
|
|
66
|
+
| Name (**including extension**) of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE |
|
|
67
|
+
|
|
68
|
+
#### File
|
|
69
|
+
|
|
70
|
+
Add `[testit]` block to your **pyproject.toml** or create **connection_config.ini** file in the root directory of the project:
|
|
71
|
+
```
|
|
72
|
+
[testit]
|
|
73
|
+
URL = URL
|
|
74
|
+
privateToken = USER_PRIVATE_TOKEN
|
|
75
|
+
projectId = PROJECT_ID
|
|
76
|
+
configurationId = CONFIGURATION_ID
|
|
77
|
+
testRunId = TEST_RUN_ID
|
|
78
|
+
testRunName = TEST_RUN_NAME
|
|
79
|
+
adapterMode = ADAPTER_MODE
|
|
80
|
+
certValidation = CERT_VALIDATION
|
|
81
|
+
automaticCreationTestCases = AUTOMATIC_CREATION_TEST_CASES
|
|
82
|
+
automaticUpdationLinksToTestCases = AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES
|
|
83
|
+
importRealtime = IMPORT_REALTIME
|
|
84
|
+
|
|
85
|
+
# This section are optional. It enables debug mode.
|
|
86
|
+
[debug]
|
|
87
|
+
tmsProxy = TMS_PROXY
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Examples
|
|
91
|
+
|
|
92
|
+
Launch with a `pyproject.toml` or `connection_config.ini` file in the root directory of the project:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
$ nose2 --testit
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If you want to enable debug mode then
|
|
99
|
+
see [How to enable debug logging?](https://github.com/testit-tms/adapters-python/tree/main/testit-python-commons)
|
|
100
|
+
|
|
101
|
+
#### Run with filter
|
|
102
|
+
To create filter by autotests you can use the Test IT CLI (use adapterMode 1 for run with filter):
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
$ export TMS_TOKEN=<YOUR_TOKEN>
|
|
106
|
+
$ testit autotests_filter
|
|
107
|
+
--url https://tms.testit.software \
|
|
108
|
+
--configuration-id 5236eb3f-7c05-46f9-a609-dc0278896464 \
|
|
109
|
+
--testrun-id 6d4ac4b7-dd67-4805-b879-18da0b89d4a8 \
|
|
110
|
+
--framework nose \
|
|
111
|
+
--output tmp/filter.txt
|
|
112
|
+
|
|
113
|
+
$ nose2 $(cat tmp/filter.txt) --testit
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Decorators
|
|
117
|
+
|
|
118
|
+
Decorators can be used to specify information about autotest.
|
|
119
|
+
|
|
120
|
+
Description of decorators:
|
|
121
|
+
|
|
122
|
+
- `testit.workItemIds` - a method that links autotests with manual tests. Receives the array of manual tests' IDs
|
|
123
|
+
- `testit.displayName` - internal autotest name (used in Test IT)
|
|
124
|
+
- `testit.externalId` - unique internal autotest ID (used in Test IT)
|
|
125
|
+
- `testit.title` - autotest name specified in the autotest card. If not specified, the name from the displayName method is used
|
|
126
|
+
- `testit.description` - autotest description specified in the autotest card
|
|
127
|
+
- `testit.labels` - tags listed in the autotest card
|
|
128
|
+
- `testit.link` - links listed in the autotest card
|
|
129
|
+
- `testit.step` - the designation of the step called in the body of the test or other step
|
|
130
|
+
- `testit.nameSpace` - directory in the TMS system (default - file's name of test)
|
|
131
|
+
- `testit.className` - subdirectory in the TMS system (default - class's name of test)
|
|
132
|
+
|
|
133
|
+
All decorators support the use of parameterization attributes
|
|
134
|
+
|
|
135
|
+
Description of methods:
|
|
136
|
+
|
|
137
|
+
- `testit.addLinks` - links in the autotest result
|
|
138
|
+
- `testit.addAttachments` - uploading files in the autotest result
|
|
139
|
+
- `testit.addMessage` - information about autotest in the autotest result
|
|
140
|
+
- `testit.step` - usage in the "with" construct to designation a step in the body of the test
|
|
141
|
+
|
|
142
|
+
### Examples
|
|
143
|
+
|
|
144
|
+
#### Simple test
|
|
145
|
+
|
|
146
|
+
```py
|
|
147
|
+
import pytest
|
|
148
|
+
import testit
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# Test with a minimal set of decorators
|
|
152
|
+
@testit.externalId('Simple_autotest2')
|
|
153
|
+
def test_2():
|
|
154
|
+
"""Simple autotest 2"""
|
|
155
|
+
assert oneStep()
|
|
156
|
+
assert twoStep()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@testit.step
|
|
160
|
+
def oneStep():
|
|
161
|
+
assert oneOneStep()
|
|
162
|
+
assert oneTwoStep()
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@testit.step
|
|
167
|
+
def twoStep():
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@testit.step('step 1.1', 'description')
|
|
172
|
+
def oneOneStep():
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@testit.step('step 2')
|
|
177
|
+
def oneTwoStep():
|
|
178
|
+
return True
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Parameterized test
|
|
182
|
+
|
|
183
|
+
```py
|
|
184
|
+
# Parameterized test with a full set of decorators
|
|
185
|
+
from os.path import join, dirname
|
|
186
|
+
|
|
187
|
+
import testit
|
|
188
|
+
from nose2.tools import params
|
|
189
|
+
|
|
190
|
+
@params(1, 2, 3)
|
|
191
|
+
@testit.workItemIds(627)
|
|
192
|
+
@testit.externalId('param {num}')
|
|
193
|
+
@testit.displayName('param {num}')
|
|
194
|
+
@testit.title('Test with params')
|
|
195
|
+
@testit.description('E2E_autotest')
|
|
196
|
+
@testit.labels('parameters', 'test')
|
|
197
|
+
@testit.links(url='https://dumps.example.com/module/JCP-777')
|
|
198
|
+
@testit.links(url='https://dumps.example.com/module/JCP-777',
|
|
199
|
+
title='JCP-777',
|
|
200
|
+
type=testit.LinkType.RELATED,
|
|
201
|
+
description='Description of JCP-777')
|
|
202
|
+
def test_nums(num):
|
|
203
|
+
assert num < 4
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
# Contributing
|
|
207
|
+
|
|
208
|
+
You can help to develop the project. Any contributions are **greatly appreciated**.
|
|
209
|
+
|
|
210
|
+
* If you have suggestions for adding or removing projects, feel free
|
|
211
|
+
to [open an issue](https://github.com/testit-tms/adapters-python/issues/new) to discuss it, or directly create a pull
|
|
212
|
+
request after you edit the *README.md* file with necessary changes.
|
|
213
|
+
* Please make sure you check your spelling and grammar.
|
|
214
|
+
* Create individual PR for each suggestion.
|
|
215
|
+
* Please also read through
|
|
216
|
+
the [Code Of Conduct](https://github.com/testit-tms/adapters-python/blob/master/CODE_OF_CONDUCT.md) before posting
|
|
217
|
+
your first idea as well.
|
|
218
|
+
|
|
219
|
+
# License
|
|
220
|
+
|
|
221
|
+
Distributed under the Apache-2.0 License.
|
|
222
|
+
See [LICENSE](https://github.com/testit-tms/adapters-python/blob/master/LICENSE.md) for more information.
|
|
223
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Test IT TMS adapter for Nose
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://pypi.python.org/pypi/testit-adapter-nose)
|
|
7
|
+
[](https://pypi.python.org/pypi/testit-adapter-nose)
|
|
8
|
+
[](https://github.com/testit-tms/adapters-python)
|
|
9
|
+
|
|
10
|
+
## Getting Started
|
|
11
|
+
|
|
12
|
+
### Installation
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
pip install testit-adapter-nose
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Configuration
|
|
21
|
+
|
|
22
|
+
| Description | File property | Environment variable |
|
|
23
|
+
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------|
|
|
24
|
+
| Location of the TMS instance | url | TMS_URL |
|
|
25
|
+
| API secret key [How to getting API secret key?](https://github.com/testit-tms/.github/tree/main/configuration#privatetoken) | privateToken | TMS_PRIVATE_TOKEN |
|
|
26
|
+
| ID of project in TMS instance [How to getting project ID?](https://github.com/testit-tms/.github/tree/main/configuration#projectid) | projectId | TMS_PROJECT_ID |
|
|
27
|
+
| ID of configuration in TMS instance [How to getting configuration ID?](https://github.com/testit-tms/.github/tree/main/configuration#configurationid) | configurationId | TMS_CONFIGURATION_ID |
|
|
28
|
+
| ID of the created test run in TMS instance.<br/>It's necessary for **adapterMode** 1 | testRunId | TMS_TEST_RUN_ID |
|
|
29
|
+
| Parameter for specifying the name of test run in TMS instance (**It's optional**). If it is not provided, it is created automatically | testRunName | TMS_TEST_RUN_NAME |
|
|
30
|
+
| Adapter mode. Default value - 1. The adapter supports following modes:<br>1 - in this mode, the adapter sends all results to the test run without filtering or [with filtering CLI](#run-with-filter)<br/>2 - in this mode, the adapter creates a new test run and sends results to the new test run | adapterMode | TMS_ADAPTER_MODE |
|
|
31
|
+
| It enables/disables certificate validation (**It's optional**). Default value - true | certValidation | TMS_CERT_VALIDATION |
|
|
32
|
+
| Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)<br/>false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES |
|
|
33
|
+
| Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will update links to test cases<br/>false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES |
|
|
34
|
+
| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:<br/>true - in this mode, the adapter will create/update each autotest in real time<br/>false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME |
|
|
35
|
+
| Url of proxy server (**It's optional**) | tmsProxy | TMS_PROXY |
|
|
36
|
+
| Name (**including extension**) of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE |
|
|
37
|
+
|
|
38
|
+
#### File
|
|
39
|
+
|
|
40
|
+
Add `[testit]` block to your **pyproject.toml** or create **connection_config.ini** file in the root directory of the project:
|
|
41
|
+
```
|
|
42
|
+
[testit]
|
|
43
|
+
URL = URL
|
|
44
|
+
privateToken = USER_PRIVATE_TOKEN
|
|
45
|
+
projectId = PROJECT_ID
|
|
46
|
+
configurationId = CONFIGURATION_ID
|
|
47
|
+
testRunId = TEST_RUN_ID
|
|
48
|
+
testRunName = TEST_RUN_NAME
|
|
49
|
+
adapterMode = ADAPTER_MODE
|
|
50
|
+
certValidation = CERT_VALIDATION
|
|
51
|
+
automaticCreationTestCases = AUTOMATIC_CREATION_TEST_CASES
|
|
52
|
+
automaticUpdationLinksToTestCases = AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES
|
|
53
|
+
importRealtime = IMPORT_REALTIME
|
|
54
|
+
|
|
55
|
+
# This section are optional. It enables debug mode.
|
|
56
|
+
[debug]
|
|
57
|
+
tmsProxy = TMS_PROXY
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Examples
|
|
61
|
+
|
|
62
|
+
Launch with a `pyproject.toml` or `connection_config.ini` file in the root directory of the project:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
$ nose2 --testit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If you want to enable debug mode then
|
|
69
|
+
see [How to enable debug logging?](https://github.com/testit-tms/adapters-python/tree/main/testit-python-commons)
|
|
70
|
+
|
|
71
|
+
#### Run with filter
|
|
72
|
+
To create filter by autotests you can use the Test IT CLI (use adapterMode 1 for run with filter):
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
$ export TMS_TOKEN=<YOUR_TOKEN>
|
|
76
|
+
$ testit autotests_filter
|
|
77
|
+
--url https://tms.testit.software \
|
|
78
|
+
--configuration-id 5236eb3f-7c05-46f9-a609-dc0278896464 \
|
|
79
|
+
--testrun-id 6d4ac4b7-dd67-4805-b879-18da0b89d4a8 \
|
|
80
|
+
--framework nose \
|
|
81
|
+
--output tmp/filter.txt
|
|
82
|
+
|
|
83
|
+
$ nose2 $(cat tmp/filter.txt) --testit
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Decorators
|
|
87
|
+
|
|
88
|
+
Decorators can be used to specify information about autotest.
|
|
89
|
+
|
|
90
|
+
Description of decorators:
|
|
91
|
+
|
|
92
|
+
- `testit.workItemIds` - a method that links autotests with manual tests. Receives the array of manual tests' IDs
|
|
93
|
+
- `testit.displayName` - internal autotest name (used in Test IT)
|
|
94
|
+
- `testit.externalId` - unique internal autotest ID (used in Test IT)
|
|
95
|
+
- `testit.title` - autotest name specified in the autotest card. If not specified, the name from the displayName method is used
|
|
96
|
+
- `testit.description` - autotest description specified in the autotest card
|
|
97
|
+
- `testit.labels` - tags listed in the autotest card
|
|
98
|
+
- `testit.link` - links listed in the autotest card
|
|
99
|
+
- `testit.step` - the designation of the step called in the body of the test or other step
|
|
100
|
+
- `testit.nameSpace` - directory in the TMS system (default - file's name of test)
|
|
101
|
+
- `testit.className` - subdirectory in the TMS system (default - class's name of test)
|
|
102
|
+
|
|
103
|
+
All decorators support the use of parameterization attributes
|
|
104
|
+
|
|
105
|
+
Description of methods:
|
|
106
|
+
|
|
107
|
+
- `testit.addLinks` - links in the autotest result
|
|
108
|
+
- `testit.addAttachments` - uploading files in the autotest result
|
|
109
|
+
- `testit.addMessage` - information about autotest in the autotest result
|
|
110
|
+
- `testit.step` - usage in the "with" construct to designation a step in the body of the test
|
|
111
|
+
|
|
112
|
+
### Examples
|
|
113
|
+
|
|
114
|
+
#### Simple test
|
|
115
|
+
|
|
116
|
+
```py
|
|
117
|
+
import pytest
|
|
118
|
+
import testit
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# Test with a minimal set of decorators
|
|
122
|
+
@testit.externalId('Simple_autotest2')
|
|
123
|
+
def test_2():
|
|
124
|
+
"""Simple autotest 2"""
|
|
125
|
+
assert oneStep()
|
|
126
|
+
assert twoStep()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@testit.step
|
|
130
|
+
def oneStep():
|
|
131
|
+
assert oneOneStep()
|
|
132
|
+
assert oneTwoStep()
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@testit.step
|
|
137
|
+
def twoStep():
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@testit.step('step 1.1', 'description')
|
|
142
|
+
def oneOneStep():
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@testit.step('step 2')
|
|
147
|
+
def oneTwoStep():
|
|
148
|
+
return True
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Parameterized test
|
|
152
|
+
|
|
153
|
+
```py
|
|
154
|
+
# Parameterized test with a full set of decorators
|
|
155
|
+
from os.path import join, dirname
|
|
156
|
+
|
|
157
|
+
import testit
|
|
158
|
+
from nose2.tools import params
|
|
159
|
+
|
|
160
|
+
@params(1, 2, 3)
|
|
161
|
+
@testit.workItemIds(627)
|
|
162
|
+
@testit.externalId('param {num}')
|
|
163
|
+
@testit.displayName('param {num}')
|
|
164
|
+
@testit.title('Test with params')
|
|
165
|
+
@testit.description('E2E_autotest')
|
|
166
|
+
@testit.labels('parameters', 'test')
|
|
167
|
+
@testit.links(url='https://dumps.example.com/module/JCP-777')
|
|
168
|
+
@testit.links(url='https://dumps.example.com/module/JCP-777',
|
|
169
|
+
title='JCP-777',
|
|
170
|
+
type=testit.LinkType.RELATED,
|
|
171
|
+
description='Description of JCP-777')
|
|
172
|
+
def test_nums(num):
|
|
173
|
+
assert num < 4
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
# Contributing
|
|
177
|
+
|
|
178
|
+
You can help to develop the project. Any contributions are **greatly appreciated**.
|
|
179
|
+
|
|
180
|
+
* If you have suggestions for adding or removing projects, feel free
|
|
181
|
+
to [open an issue](https://github.com/testit-tms/adapters-python/issues/new) to discuss it, or directly create a pull
|
|
182
|
+
request after you edit the *README.md* file with necessary changes.
|
|
183
|
+
* Please make sure you check your spelling and grammar.
|
|
184
|
+
* Create individual PR for each suggestion.
|
|
185
|
+
* Please also read through
|
|
186
|
+
the [Code Of Conduct](https://github.com/testit-tms/adapters-python/blob/master/CODE_OF_CONDUCT.md) before posting
|
|
187
|
+
your first idea as well.
|
|
188
|
+
|
|
189
|
+
# License
|
|
190
|
+
|
|
191
|
+
Distributed under the Apache-2.0 License.
|
|
192
|
+
See [LICENSE](https://github.com/testit-tms/adapters-python/blob/master/LICENSE.md) for more information.
|
|
193
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
VERSION = "3.10.8.post550"
|
|
4
|
+
|
|
5
|
+
setup(
|
|
6
|
+
name='testit-adapter-nose',
|
|
7
|
+
version=VERSION,
|
|
8
|
+
description='Nose adapter for Test IT',
|
|
9
|
+
long_description=open('README.md', "r").read(),
|
|
10
|
+
long_description_content_type="text/markdown",
|
|
11
|
+
url='https://github.com/testit-tms/adapters-python/',
|
|
12
|
+
author='Integration team',
|
|
13
|
+
author_email='integrations@testit.software',
|
|
14
|
+
license='Apache-2.0',
|
|
15
|
+
classifiers=[
|
|
16
|
+
'Programming Language :: Python :: 3',
|
|
17
|
+
'Programming Language :: Python :: 3.6',
|
|
18
|
+
'Programming Language :: Python :: 3.7',
|
|
19
|
+
'Programming Language :: Python :: 3.8',
|
|
20
|
+
'Programming Language :: Python :: 3.9',
|
|
21
|
+
'Programming Language :: Python :: 3.10',
|
|
22
|
+
'Programming Language :: Python :: 3.11',
|
|
23
|
+
'Programming Language :: Python :: 3.12',
|
|
24
|
+
],
|
|
25
|
+
py_modules=['testit_adapter_nose'],
|
|
26
|
+
packages=find_packages(where='src'),
|
|
27
|
+
package_dir={'': 'src'},
|
|
28
|
+
install_requires=['attrs', 'nose2', 'testit-python-commons==' + VERSION],
|
|
29
|
+
entry_points={
|
|
30
|
+
'nose.plugins.0.10': [
|
|
31
|
+
'testit_adapter_nose = testit_adapter_nose.plugin:TmsPlugin',
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import testit_python_commons.services as adapter
|
|
2
|
+
from testit_python_commons.services import (
|
|
3
|
+
AdapterManager,
|
|
4
|
+
StepManager)
|
|
5
|
+
from .utils import (
|
|
6
|
+
form_test,
|
|
7
|
+
get_outcome,
|
|
8
|
+
convert_executable_test_to_test_result_model
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AdapterListener(object):
|
|
13
|
+
__executable_test = None
|
|
14
|
+
|
|
15
|
+
def __init__(self, adapter_manager: AdapterManager, step_manager: StepManager, top_level_directory: str):
|
|
16
|
+
self.__adapter_manager = adapter_manager
|
|
17
|
+
self.__step_manager = step_manager
|
|
18
|
+
self.__top_level_directory = top_level_directory
|
|
19
|
+
|
|
20
|
+
def start_launch(self):
|
|
21
|
+
test_run_id = self.__adapter_manager.get_test_run_id()
|
|
22
|
+
|
|
23
|
+
self.__adapter_manager.set_test_run_id(test_run_id)
|
|
24
|
+
|
|
25
|
+
def stop_launch(self):
|
|
26
|
+
self.__adapter_manager.write_tests()
|
|
27
|
+
|
|
28
|
+
def get_tests_for_launch(self):
|
|
29
|
+
return self.__adapter_manager.get_autotests_for_launch()
|
|
30
|
+
|
|
31
|
+
def start_test(self, test):
|
|
32
|
+
self.__executable_test = form_test(test, self.__top_level_directory)
|
|
33
|
+
|
|
34
|
+
def set_outcome(self, event):
|
|
35
|
+
outcome, message, trace = get_outcome(event)
|
|
36
|
+
|
|
37
|
+
self.__executable_test['outcome'] = outcome
|
|
38
|
+
self.__executable_test['traces'] = trace
|
|
39
|
+
|
|
40
|
+
if not self.__executable_test['message']:
|
|
41
|
+
self.__executable_test['message'] = message
|
|
42
|
+
|
|
43
|
+
def stop_test(self):
|
|
44
|
+
test_results_steps = self.__step_manager.get_steps_tree()
|
|
45
|
+
self.__executable_test['stepResults'] = test_results_steps
|
|
46
|
+
|
|
47
|
+
self.__adapter_manager.write_test(
|
|
48
|
+
convert_executable_test_to_test_result_model(self.__executable_test))
|
|
49
|
+
self.__executable_test = None
|
|
50
|
+
|
|
51
|
+
@adapter.hookimpl
|
|
52
|
+
def add_link(self, link):
|
|
53
|
+
if self.__executable_test:
|
|
54
|
+
self.__executable_test['resultLinks'].append(link)
|
|
55
|
+
|
|
56
|
+
@adapter.hookimpl
|
|
57
|
+
def add_message(self, test_message):
|
|
58
|
+
if self.__executable_test:
|
|
59
|
+
self.__executable_test['message'] = str(test_message)
|
|
60
|
+
|
|
61
|
+
@adapter.hookimpl
|
|
62
|
+
def add_attachments(self, attach_paths: list or tuple):
|
|
63
|
+
if self.__executable_test:
|
|
64
|
+
self.__executable_test['attachments'] += self.__adapter_manager.load_attachments(attach_paths)
|
|
65
|
+
|
|
66
|
+
@adapter.hookimpl
|
|
67
|
+
def create_attachment(self, body, name: str):
|
|
68
|
+
if self.__executable_test:
|
|
69
|
+
self.__executable_test['attachments'] += self.__adapter_manager.create_attachment(body, name)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from nose2.events import Plugin
|
|
2
|
+
|
|
3
|
+
from testit_python_commons.services import TmsPluginManager
|
|
4
|
+
|
|
5
|
+
from .listener import AdapterListener
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TmsPlugin(Plugin):
|
|
9
|
+
configSection = 'testit'
|
|
10
|
+
commandLineSwitch = (None, 'testit', 'TMS adapter for Nose')
|
|
11
|
+
__listener = None
|
|
12
|
+
__tests_for_launch = None
|
|
13
|
+
__top_level_directory = None
|
|
14
|
+
|
|
15
|
+
def __init__(self, *args, **kwargs):
|
|
16
|
+
super(TmsPlugin, self).__init__(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
def handleDir(self, event):
|
|
19
|
+
if not self.__top_level_directory:
|
|
20
|
+
self.__top_level_directory = event.topLevelDirectory
|
|
21
|
+
|
|
22
|
+
def startTestRun(self, event):
|
|
23
|
+
self.__listener = AdapterListener(
|
|
24
|
+
TmsPluginManager.get_adapter_manager(),
|
|
25
|
+
TmsPluginManager.get_step_manager(),
|
|
26
|
+
self.__top_level_directory)
|
|
27
|
+
|
|
28
|
+
TmsPluginManager.get_plugin_manager().register(self.__listener)
|
|
29
|
+
|
|
30
|
+
self.__listener.start_launch()
|
|
31
|
+
self.__tests_for_launch = self.__listener.get_tests_for_launch()
|
|
32
|
+
|
|
33
|
+
def afterTestRun(self, event):
|
|
34
|
+
self.__listener.stop_launch()
|
|
35
|
+
|
|
36
|
+
def startTest(self, event):
|
|
37
|
+
self.__listener.start_test(event.test)
|
|
38
|
+
|
|
39
|
+
def stopTest(self, event):
|
|
40
|
+
self.__listener.stop_test()
|
|
41
|
+
|
|
42
|
+
def testOutcome(self, event):
|
|
43
|
+
self.__listener.set_outcome(event)
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import logging
|
|
3
|
+
import re
|
|
4
|
+
import os
|
|
5
|
+
from typing import List
|
|
6
|
+
from traceback import format_exception_only
|
|
7
|
+
from nose2 import (
|
|
8
|
+
util,
|
|
9
|
+
result
|
|
10
|
+
)
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
from testit_python_commons.models.link import Link
|
|
14
|
+
from testit_python_commons.models.test_result import TestResult
|
|
15
|
+
from testit_python_commons.models.outcome_type import OutcomeType
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__ARRAY_TYPES = (frozenset, list, set, tuple,)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def status_details(event):
|
|
22
|
+
message, trace = None, None
|
|
23
|
+
if event.exc_info:
|
|
24
|
+
exc_type, value, _ = event.exc_info
|
|
25
|
+
message = '\n'.join(format_exception_only(exc_type, value)) if exc_type or value else None
|
|
26
|
+
trace = ''.join(util.exc_info_to_string(event.exc_info, event.test))
|
|
27
|
+
elif event.reason:
|
|
28
|
+
message = event.reason
|
|
29
|
+
|
|
30
|
+
return message, trace
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_outcome(event):
|
|
34
|
+
outcome = None
|
|
35
|
+
message = None
|
|
36
|
+
trace = None
|
|
37
|
+
|
|
38
|
+
if event.outcome == result.PASS and event.expected:
|
|
39
|
+
outcome = OutcomeType.PASSED
|
|
40
|
+
elif event.outcome == result.PASS and not event.expected:
|
|
41
|
+
outcome = OutcomeType.PASSED
|
|
42
|
+
message = "test passes unexpectedly"
|
|
43
|
+
elif event.outcome == result.FAIL and not event.expected:
|
|
44
|
+
outcome = OutcomeType.FAILED
|
|
45
|
+
message, trace = status_details(event)
|
|
46
|
+
elif event.outcome == result.ERROR:
|
|
47
|
+
outcome = OutcomeType.BLOCKED
|
|
48
|
+
message, trace = status_details(event)
|
|
49
|
+
elif event.outcome == result.SKIP:
|
|
50
|
+
outcome = OutcomeType.SKIPPED
|
|
51
|
+
message, trace = status_details(event)
|
|
52
|
+
|
|
53
|
+
return outcome, message, trace
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def form_test(item, top_level_directory):
|
|
57
|
+
if hasattr(item, '_testFunc'):
|
|
58
|
+
function = item._testFunc
|
|
59
|
+
elif hasattr(item, '_testMethodName'):
|
|
60
|
+
function = getattr(item.__class__, item._testMethodName)
|
|
61
|
+
else:
|
|
62
|
+
raise Exception('')
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
'externalID': __get_external_id_from(item, function),
|
|
66
|
+
'autoTestName': __get_display_name_from(item, function),
|
|
67
|
+
'steps': [],
|
|
68
|
+
'stepResults': [],
|
|
69
|
+
'setUp': [],
|
|
70
|
+
'setUpResults': [],
|
|
71
|
+
'tearDown': [],
|
|
72
|
+
'tearDownResults': [],
|
|
73
|
+
'resultLinks': [],
|
|
74
|
+
'duration': 0,
|
|
75
|
+
'outcome': None,
|
|
76
|
+
'failureReasonName': None,
|
|
77
|
+
'traces': None,
|
|
78
|
+
'attachments': [],
|
|
79
|
+
'parameters': get_all_parameters(item),
|
|
80
|
+
'properties': __get_properties_from(function),
|
|
81
|
+
'namespace': __get_namespace_from(item, function),
|
|
82
|
+
'classname': __get_class_name_from(item, function),
|
|
83
|
+
'title': __get_title_from(item, function),
|
|
84
|
+
'description': __get_description_from(item, function),
|
|
85
|
+
'links': __get_links_from(item, function),
|
|
86
|
+
'labels': __get_labels_from(item, function),
|
|
87
|
+
'workItemsID': __get_work_item_ids_from(item, function),
|
|
88
|
+
'message': None,
|
|
89
|
+
'externalKey': __get_fullname(function, top_level_directory)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def __get_display_name_from(item, function):
|
|
94
|
+
display_name = __search_attribute(function, 'test_displayname')
|
|
95
|
+
|
|
96
|
+
if not display_name:
|
|
97
|
+
return function.__doc__ if \
|
|
98
|
+
function.__doc__ else function.__name__
|
|
99
|
+
|
|
100
|
+
return collect_parameters_in_string_attribute(display_name, get_all_parameters(item))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def __get_external_id_from(item, function):
|
|
104
|
+
external_id = __search_attribute(function, 'test_external_id')
|
|
105
|
+
|
|
106
|
+
if not external_id:
|
|
107
|
+
return get_hash(function.__qualname__ + function.__name__)
|
|
108
|
+
|
|
109
|
+
return collect_parameters_in_string_attribute(external_id, get_all_parameters(item))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def __get_title_from(item, function):
|
|
113
|
+
title = __search_attribute(function, 'test_title')
|
|
114
|
+
|
|
115
|
+
if not title:
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
return collect_parameters_in_string_attribute(title, get_all_parameters(item))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def __get_description_from(item, function):
|
|
122
|
+
description = __search_attribute(function, 'test_description')
|
|
123
|
+
|
|
124
|
+
if not description:
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
return collect_parameters_in_string_attribute(description, get_all_parameters(item))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def __get_namespace_from(item, function):
|
|
131
|
+
namespace = __search_attribute(function, 'test_namespace')
|
|
132
|
+
|
|
133
|
+
if not namespace:
|
|
134
|
+
return function.__module__
|
|
135
|
+
|
|
136
|
+
return collect_parameters_in_string_attribute(namespace, get_all_parameters(item))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def __get_class_name_from(item, function):
|
|
140
|
+
class_name = __search_attribute(function, 'test_classname')
|
|
141
|
+
|
|
142
|
+
if not class_name:
|
|
143
|
+
i = function.__qualname__.find('.')
|
|
144
|
+
|
|
145
|
+
if i != -1:
|
|
146
|
+
return function.__qualname__[:i]
|
|
147
|
+
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
return collect_parameters_in_string_attribute(class_name, get_all_parameters(item))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def __get_links_from(item, function):
|
|
154
|
+
links = __search_attribute(function, 'test_links')
|
|
155
|
+
|
|
156
|
+
if not links:
|
|
157
|
+
return []
|
|
158
|
+
|
|
159
|
+
return __set_parameters_to_links(links, get_all_parameters(item))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def __get_properties_from(function):
|
|
163
|
+
return __search_attribute(function, 'test_properties')
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def __set_parameters_to_links(links, all_parameters):
|
|
167
|
+
if not all_parameters:
|
|
168
|
+
return links
|
|
169
|
+
|
|
170
|
+
links_with_parameters = []
|
|
171
|
+
|
|
172
|
+
for link in links:
|
|
173
|
+
links_with_parameters.append(
|
|
174
|
+
Link()
|
|
175
|
+
.set_url(
|
|
176
|
+
collect_parameters_in_string_attribute(
|
|
177
|
+
link.get_url(),
|
|
178
|
+
all_parameters))
|
|
179
|
+
.set_title(
|
|
180
|
+
collect_parameters_in_string_attribute(
|
|
181
|
+
link.get_title(),
|
|
182
|
+
all_parameters) if link.get_title() else None)
|
|
183
|
+
.set_link_type(
|
|
184
|
+
collect_parameters_in_string_attribute(
|
|
185
|
+
link.get_link_type(),
|
|
186
|
+
all_parameters) if link.get_link_type() else None)
|
|
187
|
+
.set_description(
|
|
188
|
+
collect_parameters_in_string_attribute(
|
|
189
|
+
link.get_description(),
|
|
190
|
+
all_parameters) if link.get_description() else None))
|
|
191
|
+
|
|
192
|
+
return links_with_parameters
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def __get_labels_from(item, function):
|
|
196
|
+
test_labels = __search_attribute(function, 'test_labels')
|
|
197
|
+
|
|
198
|
+
if not test_labels:
|
|
199
|
+
return []
|
|
200
|
+
|
|
201
|
+
labels = []
|
|
202
|
+
|
|
203
|
+
for label in test_labels:
|
|
204
|
+
result = collect_parameters_in_mass_attribute(
|
|
205
|
+
label,
|
|
206
|
+
get_all_parameters(item))
|
|
207
|
+
|
|
208
|
+
if isinstance(result, __ARRAY_TYPES):
|
|
209
|
+
for label in result:
|
|
210
|
+
labels.append({
|
|
211
|
+
'name': str(label)
|
|
212
|
+
})
|
|
213
|
+
else:
|
|
214
|
+
labels.append({
|
|
215
|
+
'name': str(result)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return labels
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def __get_work_item_ids_from(item, function):
|
|
222
|
+
test_workitems_id = __search_attribute(function, 'test_workitems_id')
|
|
223
|
+
|
|
224
|
+
if not test_workitems_id:
|
|
225
|
+
return []
|
|
226
|
+
|
|
227
|
+
all_parameters = get_all_parameters(item)
|
|
228
|
+
|
|
229
|
+
if not all_parameters:
|
|
230
|
+
return test_workitems_id
|
|
231
|
+
|
|
232
|
+
result = collect_parameters_in_mass_attribute(test_workitems_id[0], all_parameters)
|
|
233
|
+
|
|
234
|
+
return map(str, result) if isinstance(result, __ARRAY_TYPES) else [str(result)]
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def __get_fullname(function, top_level_directory: str):
|
|
238
|
+
module_file_name = __get_module_file_name_by_test_function(function)
|
|
239
|
+
|
|
240
|
+
if not module_file_name:
|
|
241
|
+
return __join_nose_test_node([function.__module__, function.__qualname__])
|
|
242
|
+
|
|
243
|
+
absolute_module_path = __get_absolute_module_path_by_file_name(module_file_name)
|
|
244
|
+
module_node = __convert_absolute_module_path_to_nose_module_node(absolute_module_path, top_level_directory)
|
|
245
|
+
|
|
246
|
+
return __join_nose_test_node([module_node, function.__module__, function.__qualname__])
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def __get_module_file_name_by_test_function(test_function):
|
|
250
|
+
return __import__(test_function.__module__).__file__
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def __get_absolute_module_path_by_file_name(module_file_name: str):
|
|
254
|
+
return os.path.dirname(module_file_name)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def __convert_absolute_module_path_to_nose_module_node(absolute_module_path: str, top_level_directory: str):
|
|
258
|
+
directories_in_project = absolute_module_path.replace(top_level_directory + os.sep, '')
|
|
259
|
+
|
|
260
|
+
return directories_in_project.replace(os.sep, '.')
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def __join_nose_test_node(test_node_parts: List[str]):
|
|
264
|
+
return ".".join(test_node_parts)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def __search_attribute(function, attribute):
|
|
268
|
+
if hasattr(function, attribute):
|
|
269
|
+
return getattr(function, attribute)
|
|
270
|
+
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_all_parameters(item):
|
|
275
|
+
def _params(names, values):
|
|
276
|
+
return {name: str(value) for name, value in zip(names, values)}
|
|
277
|
+
|
|
278
|
+
test_id = item.id()
|
|
279
|
+
|
|
280
|
+
if len(test_id.split("\n")) > 1:
|
|
281
|
+
if hasattr(item, "_testFunc"):
|
|
282
|
+
wrapper_arg_spec = inspect.getfullargspec(item._testFunc)
|
|
283
|
+
arg_set, obj = wrapper_arg_spec.defaults
|
|
284
|
+
args = inspect.signature(obj).parameters.keys()
|
|
285
|
+
|
|
286
|
+
return _params(args, arg_set)
|
|
287
|
+
elif hasattr(item, "_testMethodName"):
|
|
288
|
+
method = getattr(item, item._testMethodName)
|
|
289
|
+
wrapper_arg_spec = inspect.getfullargspec(method)
|
|
290
|
+
obj, arg_set = wrapper_arg_spec.defaults
|
|
291
|
+
test_arg_spec = inspect.getfullargspec(obj)
|
|
292
|
+
args = test_arg_spec.args
|
|
293
|
+
|
|
294
|
+
return _params(args[1:], arg_set)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def collect_parameters_in_string_attribute(attribute, all_parameters):
|
|
298
|
+
param_keys = re.findall(r"\{(.*?)\}", attribute)
|
|
299
|
+
|
|
300
|
+
if len(param_keys) > 0:
|
|
301
|
+
for param_key in param_keys:
|
|
302
|
+
parameter = get_parameter(param_key, all_parameters)
|
|
303
|
+
|
|
304
|
+
if parameter is not None:
|
|
305
|
+
attribute = attribute.replace("{" + param_key + "}", str(parameter))
|
|
306
|
+
|
|
307
|
+
return attribute
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def collect_parameters_in_mass_attribute(attribute, all_parameters):
|
|
311
|
+
param_keys = re.findall(r"\{(.*?)\}", attribute)
|
|
312
|
+
|
|
313
|
+
if len(param_keys) == 1:
|
|
314
|
+
parameter = get_parameter(param_keys[0], all_parameters)
|
|
315
|
+
|
|
316
|
+
if parameter is not None:
|
|
317
|
+
return parameter
|
|
318
|
+
|
|
319
|
+
if len(param_keys) > 1:
|
|
320
|
+
logging.error(f'(For type tuple, list, set) support only one key!')
|
|
321
|
+
|
|
322
|
+
return attribute
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def get_parameter(key_for_parameter, all_parameters):
|
|
326
|
+
id_keys_in_parameter = re.findall(r'\[(.*?)\]', key_for_parameter)
|
|
327
|
+
|
|
328
|
+
if len(id_keys_in_parameter) > 1:
|
|
329
|
+
logging.error("(For type tuple, list, set, dict) support only one level!")
|
|
330
|
+
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
if len(id_keys_in_parameter) == 0:
|
|
334
|
+
if key_for_parameter not in all_parameters:
|
|
335
|
+
logging.error(f"Key for parameter {key_for_parameter} not found")
|
|
336
|
+
|
|
337
|
+
return
|
|
338
|
+
|
|
339
|
+
return all_parameters[key_for_parameter]
|
|
340
|
+
|
|
341
|
+
parameter_key = key_for_parameter.replace("[" + id_keys_in_parameter[0] + "]", "")
|
|
342
|
+
id_key_in_parameter = id_keys_in_parameter[0].strip("\'\"")
|
|
343
|
+
|
|
344
|
+
if id_key_in_parameter.isdigit() and int(id_key_in_parameter) in range(len(all_parameters[parameter_key])):
|
|
345
|
+
return all_parameters[parameter_key][int(id_key_in_parameter)]
|
|
346
|
+
|
|
347
|
+
if id_key_in_parameter.isalnum() and id_key_in_parameter in all_parameters[parameter_key].keys():
|
|
348
|
+
return all_parameters[parameter_key][id_key_in_parameter]
|
|
349
|
+
|
|
350
|
+
logging.error(f"Not key: {key_for_parameter} in run parameters or other keys problem")
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def get_hash(value: str):
|
|
354
|
+
md = hashlib.sha256(bytes(value, encoding='utf-8'))
|
|
355
|
+
return md.hexdigest()
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def convert_executable_test_to_test_result_model(executable_test: dict) -> TestResult:
|
|
359
|
+
return TestResult()\
|
|
360
|
+
.set_external_id(executable_test['externalID'])\
|
|
361
|
+
.set_autotest_name(executable_test['autoTestName'])\
|
|
362
|
+
.set_step_results(executable_test['stepResults'])\
|
|
363
|
+
.set_setup_results(executable_test['setUpResults'])\
|
|
364
|
+
.set_teardown_results(executable_test['tearDownResults'])\
|
|
365
|
+
.set_duration(executable_test['duration'])\
|
|
366
|
+
.set_outcome(executable_test['outcome'])\
|
|
367
|
+
.set_traces(executable_test['traces'])\
|
|
368
|
+
.set_attachments(executable_test['attachments'])\
|
|
369
|
+
.set_parameters(executable_test['parameters'])\
|
|
370
|
+
.set_properties(executable_test['properties'])\
|
|
371
|
+
.set_namespace(executable_test['namespace'])\
|
|
372
|
+
.set_classname(executable_test['classname'])\
|
|
373
|
+
.set_title(executable_test['title'])\
|
|
374
|
+
.set_description(executable_test['description'])\
|
|
375
|
+
.set_links(executable_test['links'])\
|
|
376
|
+
.set_result_links(executable_test['resultLinks'])\
|
|
377
|
+
.set_labels(executable_test['labels'])\
|
|
378
|
+
.set_work_item_ids(executable_test['workItemsID'])\
|
|
379
|
+
.set_message(executable_test['message'])\
|
|
380
|
+
.set_external_key(executable_test['externalKey'])
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testit-adapter-nose
|
|
3
|
+
Version: 3.10.8.post550
|
|
4
|
+
Summary: Nose adapter for Test IT
|
|
5
|
+
Home-page: https://github.com/testit-tms/adapters-python/
|
|
6
|
+
Author: Integration team
|
|
7
|
+
Author-email: integrations@testit.software
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: attrs
|
|
19
|
+
Requires-Dist: nose2
|
|
20
|
+
Requires-Dist: testit-python-commons==3.10.8.post550
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# Test IT TMS adapter for Nose
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
[](https://pypi.python.org/pypi/testit-adapter-nose)
|
|
37
|
+
[](https://pypi.python.org/pypi/testit-adapter-nose)
|
|
38
|
+
[](https://github.com/testit-tms/adapters-python)
|
|
39
|
+
|
|
40
|
+
## Getting Started
|
|
41
|
+
|
|
42
|
+
### Installation
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pip install testit-adapter-nose
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Configuration
|
|
51
|
+
|
|
52
|
+
| Description | File property | Environment variable |
|
|
53
|
+
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------|
|
|
54
|
+
| Location of the TMS instance | url | TMS_URL |
|
|
55
|
+
| API secret key [How to getting API secret key?](https://github.com/testit-tms/.github/tree/main/configuration#privatetoken) | privateToken | TMS_PRIVATE_TOKEN |
|
|
56
|
+
| ID of project in TMS instance [How to getting project ID?](https://github.com/testit-tms/.github/tree/main/configuration#projectid) | projectId | TMS_PROJECT_ID |
|
|
57
|
+
| ID of configuration in TMS instance [How to getting configuration ID?](https://github.com/testit-tms/.github/tree/main/configuration#configurationid) | configurationId | TMS_CONFIGURATION_ID |
|
|
58
|
+
| ID of the created test run in TMS instance.<br/>It's necessary for **adapterMode** 1 | testRunId | TMS_TEST_RUN_ID |
|
|
59
|
+
| Parameter for specifying the name of test run in TMS instance (**It's optional**). If it is not provided, it is created automatically | testRunName | TMS_TEST_RUN_NAME |
|
|
60
|
+
| Adapter mode. Default value - 1. The adapter supports following modes:<br>1 - in this mode, the adapter sends all results to the test run without filtering or [with filtering CLI](#run-with-filter)<br/>2 - in this mode, the adapter creates a new test run and sends results to the new test run | adapterMode | TMS_ADAPTER_MODE |
|
|
61
|
+
| It enables/disables certificate validation (**It's optional**). Default value - true | certValidation | TMS_CERT_VALIDATION |
|
|
62
|
+
| Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)<br/>false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES |
|
|
63
|
+
| Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will update links to test cases<br/>false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES |
|
|
64
|
+
| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:<br/>true - in this mode, the adapter will create/update each autotest in real time<br/>false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME |
|
|
65
|
+
| Url of proxy server (**It's optional**) | tmsProxy | TMS_PROXY |
|
|
66
|
+
| Name (**including extension**) of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE |
|
|
67
|
+
|
|
68
|
+
#### File
|
|
69
|
+
|
|
70
|
+
Add `[testit]` block to your **pyproject.toml** or create **connection_config.ini** file in the root directory of the project:
|
|
71
|
+
```
|
|
72
|
+
[testit]
|
|
73
|
+
URL = URL
|
|
74
|
+
privateToken = USER_PRIVATE_TOKEN
|
|
75
|
+
projectId = PROJECT_ID
|
|
76
|
+
configurationId = CONFIGURATION_ID
|
|
77
|
+
testRunId = TEST_RUN_ID
|
|
78
|
+
testRunName = TEST_RUN_NAME
|
|
79
|
+
adapterMode = ADAPTER_MODE
|
|
80
|
+
certValidation = CERT_VALIDATION
|
|
81
|
+
automaticCreationTestCases = AUTOMATIC_CREATION_TEST_CASES
|
|
82
|
+
automaticUpdationLinksToTestCases = AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES
|
|
83
|
+
importRealtime = IMPORT_REALTIME
|
|
84
|
+
|
|
85
|
+
# This section are optional. It enables debug mode.
|
|
86
|
+
[debug]
|
|
87
|
+
tmsProxy = TMS_PROXY
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Examples
|
|
91
|
+
|
|
92
|
+
Launch with a `pyproject.toml` or `connection_config.ini` file in the root directory of the project:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
$ nose2 --testit
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If you want to enable debug mode then
|
|
99
|
+
see [How to enable debug logging?](https://github.com/testit-tms/adapters-python/tree/main/testit-python-commons)
|
|
100
|
+
|
|
101
|
+
#### Run with filter
|
|
102
|
+
To create filter by autotests you can use the Test IT CLI (use adapterMode 1 for run with filter):
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
$ export TMS_TOKEN=<YOUR_TOKEN>
|
|
106
|
+
$ testit autotests_filter
|
|
107
|
+
--url https://tms.testit.software \
|
|
108
|
+
--configuration-id 5236eb3f-7c05-46f9-a609-dc0278896464 \
|
|
109
|
+
--testrun-id 6d4ac4b7-dd67-4805-b879-18da0b89d4a8 \
|
|
110
|
+
--framework nose \
|
|
111
|
+
--output tmp/filter.txt
|
|
112
|
+
|
|
113
|
+
$ nose2 $(cat tmp/filter.txt) --testit
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Decorators
|
|
117
|
+
|
|
118
|
+
Decorators can be used to specify information about autotest.
|
|
119
|
+
|
|
120
|
+
Description of decorators:
|
|
121
|
+
|
|
122
|
+
- `testit.workItemIds` - a method that links autotests with manual tests. Receives the array of manual tests' IDs
|
|
123
|
+
- `testit.displayName` - internal autotest name (used in Test IT)
|
|
124
|
+
- `testit.externalId` - unique internal autotest ID (used in Test IT)
|
|
125
|
+
- `testit.title` - autotest name specified in the autotest card. If not specified, the name from the displayName method is used
|
|
126
|
+
- `testit.description` - autotest description specified in the autotest card
|
|
127
|
+
- `testit.labels` - tags listed in the autotest card
|
|
128
|
+
- `testit.link` - links listed in the autotest card
|
|
129
|
+
- `testit.step` - the designation of the step called in the body of the test or other step
|
|
130
|
+
- `testit.nameSpace` - directory in the TMS system (default - file's name of test)
|
|
131
|
+
- `testit.className` - subdirectory in the TMS system (default - class's name of test)
|
|
132
|
+
|
|
133
|
+
All decorators support the use of parameterization attributes
|
|
134
|
+
|
|
135
|
+
Description of methods:
|
|
136
|
+
|
|
137
|
+
- `testit.addLinks` - links in the autotest result
|
|
138
|
+
- `testit.addAttachments` - uploading files in the autotest result
|
|
139
|
+
- `testit.addMessage` - information about autotest in the autotest result
|
|
140
|
+
- `testit.step` - usage in the "with" construct to designation a step in the body of the test
|
|
141
|
+
|
|
142
|
+
### Examples
|
|
143
|
+
|
|
144
|
+
#### Simple test
|
|
145
|
+
|
|
146
|
+
```py
|
|
147
|
+
import pytest
|
|
148
|
+
import testit
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# Test with a minimal set of decorators
|
|
152
|
+
@testit.externalId('Simple_autotest2')
|
|
153
|
+
def test_2():
|
|
154
|
+
"""Simple autotest 2"""
|
|
155
|
+
assert oneStep()
|
|
156
|
+
assert twoStep()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@testit.step
|
|
160
|
+
def oneStep():
|
|
161
|
+
assert oneOneStep()
|
|
162
|
+
assert oneTwoStep()
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@testit.step
|
|
167
|
+
def twoStep():
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@testit.step('step 1.1', 'description')
|
|
172
|
+
def oneOneStep():
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@testit.step('step 2')
|
|
177
|
+
def oneTwoStep():
|
|
178
|
+
return True
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Parameterized test
|
|
182
|
+
|
|
183
|
+
```py
|
|
184
|
+
# Parameterized test with a full set of decorators
|
|
185
|
+
from os.path import join, dirname
|
|
186
|
+
|
|
187
|
+
import testit
|
|
188
|
+
from nose2.tools import params
|
|
189
|
+
|
|
190
|
+
@params(1, 2, 3)
|
|
191
|
+
@testit.workItemIds(627)
|
|
192
|
+
@testit.externalId('param {num}')
|
|
193
|
+
@testit.displayName('param {num}')
|
|
194
|
+
@testit.title('Test with params')
|
|
195
|
+
@testit.description('E2E_autotest')
|
|
196
|
+
@testit.labels('parameters', 'test')
|
|
197
|
+
@testit.links(url='https://dumps.example.com/module/JCP-777')
|
|
198
|
+
@testit.links(url='https://dumps.example.com/module/JCP-777',
|
|
199
|
+
title='JCP-777',
|
|
200
|
+
type=testit.LinkType.RELATED,
|
|
201
|
+
description='Description of JCP-777')
|
|
202
|
+
def test_nums(num):
|
|
203
|
+
assert num < 4
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
# Contributing
|
|
207
|
+
|
|
208
|
+
You can help to develop the project. Any contributions are **greatly appreciated**.
|
|
209
|
+
|
|
210
|
+
* If you have suggestions for adding or removing projects, feel free
|
|
211
|
+
to [open an issue](https://github.com/testit-tms/adapters-python/issues/new) to discuss it, or directly create a pull
|
|
212
|
+
request after you edit the *README.md* file with necessary changes.
|
|
213
|
+
* Please make sure you check your spelling and grammar.
|
|
214
|
+
* Create individual PR for each suggestion.
|
|
215
|
+
* Please also read through
|
|
216
|
+
the [Code Of Conduct](https://github.com/testit-tms/adapters-python/blob/master/CODE_OF_CONDUCT.md) before posting
|
|
217
|
+
your first idea as well.
|
|
218
|
+
|
|
219
|
+
# License
|
|
220
|
+
|
|
221
|
+
Distributed under the Apache-2.0 License.
|
|
222
|
+
See [LICENSE](https://github.com/testit-tms/adapters-python/blob/master/LICENSE.md) for more information.
|
|
223
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
src/testit_adapter_nose/__init__.py
|
|
4
|
+
src/testit_adapter_nose/listener.py
|
|
5
|
+
src/testit_adapter_nose/plugin.py
|
|
6
|
+
src/testit_adapter_nose/utils.py
|
|
7
|
+
src/testit_adapter_nose.egg-info/PKG-INFO
|
|
8
|
+
src/testit_adapter_nose.egg-info/SOURCES.txt
|
|
9
|
+
src/testit_adapter_nose.egg-info/dependency_links.txt
|
|
10
|
+
src/testit_adapter_nose.egg-info/entry_points.txt
|
|
11
|
+
src/testit_adapter_nose.egg-info/requires.txt
|
|
12
|
+
src/testit_adapter_nose.egg-info/top_level.txt
|
|
13
|
+
tests/test_plugin.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
testit_adapter_nose
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pytest_mock import MockerFixture
|
|
3
|
+
|
|
4
|
+
from testit_adapter_nose.plugin import TmsPlugin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestTmsPlugin:
|
|
8
|
+
def test_handleDir(self, mocker: MockerFixture):
|
|
9
|
+
plugin = TmsPlugin()
|
|
10
|
+
mock_event = mocker.Mock()
|
|
11
|
+
mock_event.topLevelDirectory = "/test/directory"
|
|
12
|
+
|
|
13
|
+
assert plugin._TmsPlugin__top_level_directory is None
|
|
14
|
+
|
|
15
|
+
plugin.handleDir(mock_event)
|
|
16
|
+
|
|
17
|
+
assert plugin._TmsPlugin__top_level_directory == "/test/directory"
|
|
18
|
+
|
|
19
|
+
# Calling it again should not change the value
|
|
20
|
+
mock_event_another = mocker.Mock()
|
|
21
|
+
mock_event_another.topLevelDirectory = "/another/directory"
|
|
22
|
+
plugin.handleDir(mock_event_another)
|
|
23
|
+
|
|
24
|
+
assert plugin._TmsPlugin__top_level_directory == "/test/directory"
|