robotframework-mock 0.1.0__tar.gz → 0.2.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.
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/PKG-INFO +33 -3
- robotframework_mock-0.1.0/src/robotframework_mock.egg-info/PKG-INFO → robotframework_mock-0.2.0/README.md +191 -179
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/setup.cfg +1 -1
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/src/MockLibrary/__init__.py +51 -3
- robotframework_mock-0.2.0/src/MockResource/__init__.py +113 -0
- robotframework_mock-0.1.0/README.md → robotframework_mock-0.2.0/src/robotframework_mock.egg-info/PKG-INFO +209 -161
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/src/robotframework_mock.egg-info/SOURCES.txt +1 -0
- robotframework_mock-0.2.0/src/robotframework_mock.egg-info/top_level.txt +2 -0
- robotframework_mock-0.1.0/src/robotframework_mock.egg-info/top_level.txt +0 -1
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/LICENSE +0 -0
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/MANIFEST.in +0 -0
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/pyproject.toml +0 -0
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/setup.py +0 -0
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/src/robotframework_mock.egg-info/dependency_links.txt +0 -0
- {robotframework_mock-0.1.0 → robotframework_mock-0.2.0}/src/robotframework_mock.egg-info/requires.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-mock
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Robot Framework library for mocking keywords in unit tests
|
|
5
5
|
Home-page: https://github.com/olesz/robotframework-mock
|
|
6
6
|
Author: Lajos Olah
|
|
@@ -46,6 +46,7 @@ pip install -r requirements-dev.txt
|
|
|
46
46
|
## Features
|
|
47
47
|
|
|
48
48
|
- Mock keywords from any Robot Framework library
|
|
49
|
+
- Mock keywords from Robot Framework resource files
|
|
49
50
|
- Mock Robot Framework's BuiltIn keywords
|
|
50
51
|
- Support for keywords with custom names via @keyword decorator
|
|
51
52
|
- Verify keyword calls and call counts
|
|
@@ -119,6 +120,23 @@ Test Multiple Mocks
|
|
|
119
120
|
MockReq.Reset Mocks
|
|
120
121
|
```
|
|
121
122
|
|
|
123
|
+
### Mock Resource Keywords
|
|
124
|
+
|
|
125
|
+
Mock keywords from Robot Framework resource files:
|
|
126
|
+
|
|
127
|
+
```robot
|
|
128
|
+
*** Settings ***
|
|
129
|
+
Resource my_resource.robot
|
|
130
|
+
Library MockResource my_resource.robot WITH NAME MockRes
|
|
131
|
+
|
|
132
|
+
*** Test Cases ***
|
|
133
|
+
Test Resource Keyword Mock
|
|
134
|
+
MockRes.Mock Keyword My Custom Keyword return_value=mocked_value
|
|
135
|
+
${result}= My Custom Keyword
|
|
136
|
+
Should Be Equal ${result} mocked_value
|
|
137
|
+
MockRes.Reset Mocks
|
|
138
|
+
```
|
|
139
|
+
|
|
122
140
|
## Keywords
|
|
123
141
|
|
|
124
142
|
### Mock Keyword
|
|
@@ -159,6 +177,8 @@ MockDB.Verify Keyword Called execute_sql times=1
|
|
|
159
177
|
|
|
160
178
|
## How It Works
|
|
161
179
|
|
|
180
|
+
### MockLibrary
|
|
181
|
+
|
|
162
182
|
MockLibrary dynamically replaces keyword implementations:
|
|
163
183
|
1. Wraps the target library instance
|
|
164
184
|
2. Resolves keyword names to function names (handles @keyword decorator)
|
|
@@ -168,11 +188,21 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
168
188
|
6. Tracks call counts for verification
|
|
169
189
|
7. Raises AttributeError if attempting to mock a non-existent keyword
|
|
170
190
|
|
|
191
|
+
### MockResource
|
|
192
|
+
|
|
193
|
+
MockResource patches Robot Framework's keyword execution:
|
|
194
|
+
1. Patches the Namespace.get_runner method
|
|
195
|
+
2. Intercepts keyword execution for the specified resource file
|
|
196
|
+
3. Replaces keyword body with Return statement containing mocked value
|
|
197
|
+
4. Tracks call counts for verification
|
|
198
|
+
5. Restores original keyword body on reset
|
|
199
|
+
|
|
171
200
|
## Notes
|
|
172
201
|
|
|
173
|
-
-
|
|
202
|
+
- Both libraries use `ROBOT_LIBRARY_SCOPE = 'GLOBAL'` to maintain state across test cases
|
|
174
203
|
- Built on Python's unittest.mock.Mock for robust mocking capabilities
|
|
175
|
-
-
|
|
204
|
+
- MockLibrary supports any Robot Framework library, including BuiltIn
|
|
205
|
+
- MockResource works with resource files by patching the keyword execution pipeline
|
|
176
206
|
|
|
177
207
|
## License
|
|
178
208
|
|
|
@@ -1,179 +1,191 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
###
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
###
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```robot
|
|
61
|
-
***
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Library
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Test
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Keywords
|
|
123
|
-
|
|
124
|
-
### Mock Keyword
|
|
125
|
-
|
|
126
|
-
Mock a keyword with a return value or side effect.
|
|
127
|
-
|
|
128
|
-
**Arguments:**
|
|
129
|
-
- `keyword_name` - Name of the keyword to mock
|
|
130
|
-
- `return_value` - Value to return when called (optional)
|
|
131
|
-
- `side_effect` - Callable to execute instead (optional)
|
|
132
|
-
|
|
133
|
-
**Example:**
|
|
134
|
-
```robot
|
|
135
|
-
MockDB.Mock Keyword query return_value=test_data
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Reset Mocks
|
|
139
|
-
|
|
140
|
-
Restore all mocked keywords to their original implementations.
|
|
141
|
-
|
|
142
|
-
**Example:**
|
|
143
|
-
```robot
|
|
144
|
-
MockDB.Reset Mocks
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Verify Keyword Called
|
|
148
|
-
|
|
149
|
-
Verify a keyword was called, optionally checking call count.
|
|
150
|
-
|
|
151
|
-
**Arguments:**
|
|
152
|
-
- `keyword_name` - Name of the keyword to verify
|
|
153
|
-
- `times` - Expected number of calls (optional)
|
|
154
|
-
|
|
155
|
-
**Example:**
|
|
156
|
-
```robot
|
|
157
|
-
MockDB.Verify Keyword Called execute_sql times=1
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## How It Works
|
|
161
|
-
|
|
162
|
-
MockLibrary
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
1
|
+
# robotframework-mock
|
|
2
|
+
|
|
3
|
+
A Robot Framework library for mocking keywords in unit tests.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From PyPI (once published)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install robotframework-mock
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### From source
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/yourusername/robotframework-mock.git
|
|
17
|
+
cd robotframework-mock
|
|
18
|
+
pip install .
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### For development
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -e .
|
|
25
|
+
pip install -r requirements-dev.txt
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- Mock keywords from any Robot Framework library
|
|
31
|
+
- Mock keywords from Robot Framework resource files
|
|
32
|
+
- Mock Robot Framework's BuiltIn keywords
|
|
33
|
+
- Support for keywords with custom names via @keyword decorator
|
|
34
|
+
- Verify keyword calls and call counts
|
|
35
|
+
- Simple API with three main keywords
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Mock Library Keywords
|
|
40
|
+
|
|
41
|
+
Import the library you want to mock, then create a MockLibrary instance for it:
|
|
42
|
+
|
|
43
|
+
```robot
|
|
44
|
+
*** Settings ***
|
|
45
|
+
Library DatabaseLibrary
|
|
46
|
+
Library MockLibrary DatabaseLibrary WITH NAME MockDB
|
|
47
|
+
|
|
48
|
+
*** Test Cases ***
|
|
49
|
+
Test With Mocked Keyword
|
|
50
|
+
MockDB.Mock Keyword query return_value=test_data
|
|
51
|
+
${result}= DatabaseLibrary.Query SELECT * FROM users
|
|
52
|
+
Should Be Equal ${result} test_data
|
|
53
|
+
MockDB.Reset Mocks
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Verify Calls
|
|
57
|
+
|
|
58
|
+
Verify that a keyword was called and optionally check the call count:
|
|
59
|
+
|
|
60
|
+
```robot
|
|
61
|
+
*** Test Cases ***
|
|
62
|
+
Test Keyword Was Called
|
|
63
|
+
MockDB.Mock Keyword execute_sql return_value=${None}
|
|
64
|
+
Process User Registration
|
|
65
|
+
MockDB.Verify Keyword Called execute_sql times=1
|
|
66
|
+
MockDB.Reset Mocks
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Mock BuiltIn Keywords
|
|
70
|
+
|
|
71
|
+
Mock Robot Framework's built-in keywords using the same MockLibrary with "BuiltIn" as the library name:
|
|
72
|
+
|
|
73
|
+
```robot
|
|
74
|
+
*** Settings ***
|
|
75
|
+
Library MockLibrary BuiltIn WITH NAME MockBin
|
|
76
|
+
|
|
77
|
+
*** Test Cases ***
|
|
78
|
+
Test BuiltIn Mock
|
|
79
|
+
MockBin.Mock Keyword Convert To Binary return_value=test_data
|
|
80
|
+
${result}= Convert To Binary aaa
|
|
81
|
+
Should Be Equal ${result} test_data
|
|
82
|
+
MockBin.Reset Mocks
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Mock Multiple Libraries
|
|
86
|
+
|
|
87
|
+
You can mock multiple libraries in the same test:
|
|
88
|
+
|
|
89
|
+
```robot
|
|
90
|
+
*** Settings ***
|
|
91
|
+
Library DatabaseLibrary
|
|
92
|
+
Library RequestsLibrary
|
|
93
|
+
Library MockLibrary DatabaseLibrary WITH NAME MockDB
|
|
94
|
+
Library MockLibrary RequestsLibrary WITH NAME MockReq
|
|
95
|
+
|
|
96
|
+
*** Test Cases ***
|
|
97
|
+
Test Multiple Mocks
|
|
98
|
+
MockDB.Mock Keyword query return_value=user_data
|
|
99
|
+
MockReq.Mock Keyword get return_value=api_response
|
|
100
|
+
# Your test code here
|
|
101
|
+
MockDB.Reset Mocks
|
|
102
|
+
MockReq.Reset Mocks
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Mock Resource Keywords
|
|
106
|
+
|
|
107
|
+
Mock keywords from Robot Framework resource files:
|
|
108
|
+
|
|
109
|
+
```robot
|
|
110
|
+
*** Settings ***
|
|
111
|
+
Resource my_resource.robot
|
|
112
|
+
Library MockResource my_resource.robot WITH NAME MockRes
|
|
113
|
+
|
|
114
|
+
*** Test Cases ***
|
|
115
|
+
Test Resource Keyword Mock
|
|
116
|
+
MockRes.Mock Keyword My Custom Keyword return_value=mocked_value
|
|
117
|
+
${result}= My Custom Keyword
|
|
118
|
+
Should Be Equal ${result} mocked_value
|
|
119
|
+
MockRes.Reset Mocks
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Keywords
|
|
123
|
+
|
|
124
|
+
### Mock Keyword
|
|
125
|
+
|
|
126
|
+
Mock a keyword with a return value or side effect.
|
|
127
|
+
|
|
128
|
+
**Arguments:**
|
|
129
|
+
- `keyword_name` - Name of the keyword to mock
|
|
130
|
+
- `return_value` - Value to return when called (optional)
|
|
131
|
+
- `side_effect` - Callable to execute instead (optional)
|
|
132
|
+
|
|
133
|
+
**Example:**
|
|
134
|
+
```robot
|
|
135
|
+
MockDB.Mock Keyword query return_value=test_data
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Reset Mocks
|
|
139
|
+
|
|
140
|
+
Restore all mocked keywords to their original implementations.
|
|
141
|
+
|
|
142
|
+
**Example:**
|
|
143
|
+
```robot
|
|
144
|
+
MockDB.Reset Mocks
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Verify Keyword Called
|
|
148
|
+
|
|
149
|
+
Verify a keyword was called, optionally checking call count.
|
|
150
|
+
|
|
151
|
+
**Arguments:**
|
|
152
|
+
- `keyword_name` - Name of the keyword to verify
|
|
153
|
+
- `times` - Expected number of calls (optional)
|
|
154
|
+
|
|
155
|
+
**Example:**
|
|
156
|
+
```robot
|
|
157
|
+
MockDB.Verify Keyword Called execute_sql times=1
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## How It Works
|
|
161
|
+
|
|
162
|
+
### MockLibrary
|
|
163
|
+
|
|
164
|
+
MockLibrary dynamically replaces keyword implementations:
|
|
165
|
+
1. Wraps the target library instance
|
|
166
|
+
2. Resolves keyword names to function names (handles @keyword decorator)
|
|
167
|
+
3. Stores original methods before mocking
|
|
168
|
+
4. Replaces methods with mock implementations using Python's unittest.mock.Mock
|
|
169
|
+
5. Returns mocked values or executes side effects
|
|
170
|
+
6. Tracks call counts for verification
|
|
171
|
+
7. Raises AttributeError if attempting to mock a non-existent keyword
|
|
172
|
+
|
|
173
|
+
### MockResource
|
|
174
|
+
|
|
175
|
+
MockResource patches Robot Framework's keyword execution:
|
|
176
|
+
1. Patches the Namespace.get_runner method
|
|
177
|
+
2. Intercepts keyword execution for the specified resource file
|
|
178
|
+
3. Replaces keyword body with Return statement containing mocked value
|
|
179
|
+
4. Tracks call counts for verification
|
|
180
|
+
5. Restores original keyword body on reset
|
|
181
|
+
|
|
182
|
+
## Notes
|
|
183
|
+
|
|
184
|
+
- Both libraries use `ROBOT_LIBRARY_SCOPE = 'GLOBAL'` to maintain state across test cases
|
|
185
|
+
- Built on Python's unittest.mock.Mock for robust mocking capabilities
|
|
186
|
+
- MockLibrary supports any Robot Framework library, including BuiltIn
|
|
187
|
+
- MockResource works with resource files by patching the keyword execution pipeline
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
See LICENSE file for details.
|
|
@@ -6,10 +6,19 @@ from unittest.mock import Mock
|
|
|
6
6
|
from robot.api.deco import keyword
|
|
7
7
|
from robot.libraries.BuiltIn import BuiltIn
|
|
8
8
|
|
|
9
|
-
__version__ = "1.0.0"
|
|
10
|
-
|
|
11
9
|
|
|
12
10
|
def _get_library_instance(library_name_or_alias):
|
|
11
|
+
"""Retrieve a library instance from Robot Framework's runtime.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
library_name_or_alias: Name or alias of the library to retrieve
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
The library instance object
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
RuntimeError: If the library is not found in Robot Framework
|
|
21
|
+
"""
|
|
13
22
|
lib = BuiltIn().get_library_instance(library_name_or_alias)
|
|
14
23
|
if not lib:
|
|
15
24
|
raise RuntimeError(
|
|
@@ -34,8 +43,17 @@ class MockLibrary():
|
|
|
34
43
|
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
|
|
35
44
|
|
|
36
45
|
def __init__(self, library_name_or_alias: str, lib: Any = None):
|
|
46
|
+
"""Initialize MockLibrary with a target library to mock.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
library_name_or_alias: Name or alias of the library to mock
|
|
50
|
+
lib: Optional library instance (for testing purposes)
|
|
51
|
+
"""
|
|
52
|
+
# Store original method references before mocking
|
|
37
53
|
self._original_methods = {}
|
|
54
|
+
# Store Mock objects for verification
|
|
38
55
|
self._mocks = {}
|
|
56
|
+
# Get or set the library instance to mock
|
|
39
57
|
if lib:
|
|
40
58
|
self._library_instance = lib
|
|
41
59
|
else:
|
|
@@ -48,17 +66,26 @@ class MockLibrary():
|
|
|
48
66
|
):
|
|
49
67
|
"""Mock a keyword from the wrapped library.
|
|
50
68
|
|
|
69
|
+
Args:
|
|
70
|
+
keyword_name: Name of the keyword to mock (case-insensitive)
|
|
71
|
+
return_value: Value to return when the keyword is called
|
|
72
|
+
side_effect: Callable to execute instead of returning a value
|
|
73
|
+
|
|
51
74
|
Example:
|
|
52
75
|
| MockDB.Mock Keyword | query | return_value=test_data |
|
|
53
76
|
"""
|
|
54
77
|
lib = self._library_instance
|
|
78
|
+
# Convert keyword name to method name format (lowercase with underscores)
|
|
55
79
|
method_name = keyword_name.lower().replace(' ', '_')
|
|
56
80
|
|
|
81
|
+
# Only store original method once per keyword
|
|
57
82
|
if method_name not in self._original_methods:
|
|
58
83
|
original_method = None
|
|
84
|
+
# Try direct attribute lookup first
|
|
59
85
|
try:
|
|
60
86
|
original_method = getattr(lib, method_name)
|
|
61
87
|
except AttributeError:
|
|
88
|
+
# If not found, search for methods with @keyword decorator custom names
|
|
62
89
|
for name, method in inspect.getmembers(
|
|
63
90
|
lib, inspect.ismethod
|
|
64
91
|
):
|
|
@@ -67,36 +94,47 @@ class MockLibrary():
|
|
|
67
94
|
original_method = getattr(lib, name)
|
|
68
95
|
method_name = name
|
|
69
96
|
break
|
|
97
|
+
# Raise error if keyword doesn't exist
|
|
70
98
|
if not original_method:
|
|
71
99
|
raise AttributeError(f"Keyword '{keyword_name}' not found in {lib}")
|
|
72
100
|
self._original_methods[method_name] = original_method
|
|
73
101
|
|
|
102
|
+
# Create Mock object with specified behavior
|
|
74
103
|
mock = Mock(return_value=return_value, side_effect=side_effect)
|
|
75
104
|
self._mocks[method_name] = mock
|
|
76
105
|
|
|
106
|
+
# Replace the method on the class or instance
|
|
77
107
|
try:
|
|
108
|
+
# Try to set on the class for bound methods
|
|
78
109
|
owner_class = self._original_methods[method_name].__self__.__class__
|
|
79
110
|
setattr(owner_class, method_name, mock)
|
|
80
111
|
except AttributeError:
|
|
112
|
+
# Fall back to setting on the instance
|
|
81
113
|
setattr(lib, method_name, mock)
|
|
82
114
|
|
|
83
|
-
|
|
84
115
|
return mock
|
|
85
116
|
|
|
86
117
|
@keyword
|
|
87
118
|
def reset_mocks(self):
|
|
88
119
|
"""Reset all mocks to their original implementations.
|
|
89
120
|
|
|
121
|
+
Restores all mocked keywords to their original behavior and clears
|
|
122
|
+
all tracking data.
|
|
123
|
+
|
|
90
124
|
Example:
|
|
91
125
|
| MockDB.Reset Mocks |
|
|
92
126
|
"""
|
|
127
|
+
# Restore each mocked method to its original implementation
|
|
93
128
|
for method_name, original_method in self._original_methods.items():
|
|
94
129
|
try:
|
|
130
|
+
# Restore on the class for bound methods
|
|
95
131
|
owner_class = original_method.__self__.__class__
|
|
96
132
|
setattr(owner_class, method_name, original_method)
|
|
97
133
|
except AttributeError:
|
|
134
|
+
# Restore on the instance
|
|
98
135
|
setattr(self._library_instance, method_name, original_method)
|
|
99
136
|
|
|
137
|
+
# Clear all tracking dictionaries
|
|
100
138
|
self._mocks.clear()
|
|
101
139
|
self._original_methods.clear()
|
|
102
140
|
|
|
@@ -104,13 +142,23 @@ class MockLibrary():
|
|
|
104
142
|
def verify_keyword_called(self, keyword_name: str, times: int = None):
|
|
105
143
|
"""Verify that a mocked keyword was called.
|
|
106
144
|
|
|
145
|
+
Args:
|
|
146
|
+
keyword_name: Name of the keyword to verify
|
|
147
|
+
times: Expected number of calls (optional, if None just checks it was called)
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
AssertionError: If keyword was not mocked or call count doesn't match
|
|
151
|
+
|
|
107
152
|
Example:
|
|
108
153
|
| MockDB.Verify Keyword Called | execute_sql | times=1 |
|
|
109
154
|
"""
|
|
155
|
+
# Convert keyword name to method name format
|
|
110
156
|
method_name = keyword_name.lower().replace(' ', '_')
|
|
157
|
+
# Check if the keyword was mocked
|
|
111
158
|
if method_name not in self._mocks:
|
|
112
159
|
raise AssertionError(f"Keyword '{keyword_name}' was not mocked")
|
|
113
160
|
|
|
161
|
+
# Verify call count if specified
|
|
114
162
|
mock = self._mocks[method_name]
|
|
115
163
|
if times is not None and mock.call_count != times:
|
|
116
164
|
raise AssertionError(f"Expected {times} calls, got {mock.call_count}")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Mock resource for Robot Framework keyword mocking in unit tests."""
|
|
2
|
+
# pylint: disable=invalid-name
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
from unittest.mock import Mock
|
|
5
|
+
|
|
6
|
+
from robot.api.deco import keyword
|
|
7
|
+
from robot.libraries.BuiltIn import BuiltIn
|
|
8
|
+
from robot.running import Return
|
|
9
|
+
from robot.running.namespace import Namespace
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MockResource:
|
|
13
|
+
"""Mock keywords from Robot Framework resource files for unit testing.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
| Library | MockResource | my_resource.robot | WITH NAME | MockRes |
|
|
17
|
+
| MockRes.Mock Keyword | My Keyword | return_value=test_data |
|
|
18
|
+
| My Keyword |
|
|
19
|
+
| MockRes.Reset Mocks |
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, source):
|
|
23
|
+
self._source = source
|
|
24
|
+
self._original_get_runner = Namespace.get_runner
|
|
25
|
+
self._original_items = {}
|
|
26
|
+
self._mocks = {}
|
|
27
|
+
self._install_patch()
|
|
28
|
+
|
|
29
|
+
def _install_patch(self):
|
|
30
|
+
original_get_runner = self._original_get_runner
|
|
31
|
+
active_mocks = self._mocks
|
|
32
|
+
source = self._source
|
|
33
|
+
|
|
34
|
+
def patched_get_runner(self, keyword_name, recommend_on_failure):
|
|
35
|
+
keyword_runner = original_get_runner(self, keyword_name, recommend_on_failure)
|
|
36
|
+
resource_file = getattr(keyword_runner.keyword, "source", None)
|
|
37
|
+
|
|
38
|
+
if source not in str(resource_file):
|
|
39
|
+
return keyword_runner
|
|
40
|
+
|
|
41
|
+
mock = active_mocks.get(keyword_name)
|
|
42
|
+
if mock:
|
|
43
|
+
original_run = keyword_runner.run
|
|
44
|
+
def patched_run(data, result, context, run):
|
|
45
|
+
mock_result = mock(data.args)
|
|
46
|
+
keyword_runner.keyword.body._items = [Return(values=[mock_result])] # pylint: disable=protected-access
|
|
47
|
+
return original_run(data, result, context, run)
|
|
48
|
+
keyword_runner.run = patched_run
|
|
49
|
+
|
|
50
|
+
return keyword_runner
|
|
51
|
+
|
|
52
|
+
Namespace.get_runner = patched_get_runner
|
|
53
|
+
|
|
54
|
+
@keyword
|
|
55
|
+
def mock_keyword(
|
|
56
|
+
self, keyword_name: str,
|
|
57
|
+
return_value: Any = None, side_effect: Callable = None
|
|
58
|
+
):
|
|
59
|
+
"""Mock a keyword from the resource file.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
keyword_name: Name of the keyword to mock
|
|
63
|
+
return_value: Value to return when the keyword is called
|
|
64
|
+
side_effect: Callable to execute instead of returning a value
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
| MockRes.Mock Keyword | My Keyword | return_value=test_data |
|
|
68
|
+
"""
|
|
69
|
+
keyword_runner = BuiltIn()._namespace.get_runner(keyword_name, True) # pylint: disable=protected-access
|
|
70
|
+
resource_file = getattr(keyword_runner.keyword, "source", None)
|
|
71
|
+
|
|
72
|
+
if self._source not in str(resource_file):
|
|
73
|
+
raise AttributeError("fKeyword '{keyword_name}' not found in {self._source}")
|
|
74
|
+
|
|
75
|
+
self._original_items[keyword_name] = keyword_runner.keyword.body._items # pylint: disable=protected-access
|
|
76
|
+
mock = Mock(return_value=return_value, side_effect=side_effect)
|
|
77
|
+
self._mocks[keyword_name] = mock
|
|
78
|
+
|
|
79
|
+
@keyword
|
|
80
|
+
def reset_mocks(self):
|
|
81
|
+
"""Reset all mocks to their original implementations.
|
|
82
|
+
|
|
83
|
+
Restores all mocked keywords to their original behavior.
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
| MockRes.Reset Mocks |
|
|
87
|
+
"""
|
|
88
|
+
self._mocks.clear()
|
|
89
|
+
for keyword_name, items in self._original_items.items():
|
|
90
|
+
keyword_runner = BuiltIn()._namespace.get_runner(keyword_name, True) # pylint: disable=protected-access
|
|
91
|
+
keyword_runner.keyword.body._items = items # pylint: disable=protected-access
|
|
92
|
+
self._original_items.clear()
|
|
93
|
+
|
|
94
|
+
@keyword
|
|
95
|
+
def verify_keyword_called(self, keyword_name: str, times: int = None):
|
|
96
|
+
"""Verify that a mocked keyword was called.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
keyword_name: Name of the keyword to verify
|
|
100
|
+
times: Expected number of calls (optional)
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
AssertionError: If keyword was not mocked or call count doesn't match
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
| MockDB.Verify Keyword Called | Execute Sql | times=1 |
|
|
107
|
+
"""
|
|
108
|
+
if keyword_name not in self._mocks:
|
|
109
|
+
raise AssertionError(f"Keyword '{keyword_name}' was not mocked")
|
|
110
|
+
|
|
111
|
+
mock = self._mocks[keyword_name]
|
|
112
|
+
if times is not None and mock.call_count != times:
|
|
113
|
+
raise AssertionError(f"Expected {times} calls, got {mock.call_count}")
|
|
@@ -1,161 +1,209 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Library MockLibrary
|
|
94
|
-
|
|
95
|
-
*** Test Cases ***
|
|
96
|
-
Test
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robotframework-mock
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A Robot Framework library for mocking keywords in unit tests
|
|
5
|
+
Home-page: https://github.com/olesz/robotframework-mock
|
|
6
|
+
Author: Lajos Olah
|
|
7
|
+
Author-email: lajos.olah.jr@gmail.com
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Framework :: Robot Framework
|
|
12
|
+
Classifier: Framework :: Robot Framework :: Library
|
|
13
|
+
Requires-Python: >=3.7
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: robotframework>=7.0
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# robotframework-mock
|
|
20
|
+
|
|
21
|
+
A Robot Framework library for mocking keywords in unit tests.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
### From PyPI (once published)
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install robotframework-mock
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### From source
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/yourusername/robotframework-mock.git
|
|
35
|
+
cd robotframework-mock
|
|
36
|
+
pip install .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### For development
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install -e .
|
|
43
|
+
pip install -r requirements-dev.txt
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- Mock keywords from any Robot Framework library
|
|
49
|
+
- Mock keywords from Robot Framework resource files
|
|
50
|
+
- Mock Robot Framework's BuiltIn keywords
|
|
51
|
+
- Support for keywords with custom names via @keyword decorator
|
|
52
|
+
- Verify keyword calls and call counts
|
|
53
|
+
- Simple API with three main keywords
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
### Mock Library Keywords
|
|
58
|
+
|
|
59
|
+
Import the library you want to mock, then create a MockLibrary instance for it:
|
|
60
|
+
|
|
61
|
+
```robot
|
|
62
|
+
*** Settings ***
|
|
63
|
+
Library DatabaseLibrary
|
|
64
|
+
Library MockLibrary DatabaseLibrary WITH NAME MockDB
|
|
65
|
+
|
|
66
|
+
*** Test Cases ***
|
|
67
|
+
Test With Mocked Keyword
|
|
68
|
+
MockDB.Mock Keyword query return_value=test_data
|
|
69
|
+
${result}= DatabaseLibrary.Query SELECT * FROM users
|
|
70
|
+
Should Be Equal ${result} test_data
|
|
71
|
+
MockDB.Reset Mocks
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Verify Calls
|
|
75
|
+
|
|
76
|
+
Verify that a keyword was called and optionally check the call count:
|
|
77
|
+
|
|
78
|
+
```robot
|
|
79
|
+
*** Test Cases ***
|
|
80
|
+
Test Keyword Was Called
|
|
81
|
+
MockDB.Mock Keyword execute_sql return_value=${None}
|
|
82
|
+
Process User Registration
|
|
83
|
+
MockDB.Verify Keyword Called execute_sql times=1
|
|
84
|
+
MockDB.Reset Mocks
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Mock BuiltIn Keywords
|
|
88
|
+
|
|
89
|
+
Mock Robot Framework's built-in keywords using the same MockLibrary with "BuiltIn" as the library name:
|
|
90
|
+
|
|
91
|
+
```robot
|
|
92
|
+
*** Settings ***
|
|
93
|
+
Library MockLibrary BuiltIn WITH NAME MockBin
|
|
94
|
+
|
|
95
|
+
*** Test Cases ***
|
|
96
|
+
Test BuiltIn Mock
|
|
97
|
+
MockBin.Mock Keyword Convert To Binary return_value=test_data
|
|
98
|
+
${result}= Convert To Binary aaa
|
|
99
|
+
Should Be Equal ${result} test_data
|
|
100
|
+
MockBin.Reset Mocks
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Mock Multiple Libraries
|
|
104
|
+
|
|
105
|
+
You can mock multiple libraries in the same test:
|
|
106
|
+
|
|
107
|
+
```robot
|
|
108
|
+
*** Settings ***
|
|
109
|
+
Library DatabaseLibrary
|
|
110
|
+
Library RequestsLibrary
|
|
111
|
+
Library MockLibrary DatabaseLibrary WITH NAME MockDB
|
|
112
|
+
Library MockLibrary RequestsLibrary WITH NAME MockReq
|
|
113
|
+
|
|
114
|
+
*** Test Cases ***
|
|
115
|
+
Test Multiple Mocks
|
|
116
|
+
MockDB.Mock Keyword query return_value=user_data
|
|
117
|
+
MockReq.Mock Keyword get return_value=api_response
|
|
118
|
+
# Your test code here
|
|
119
|
+
MockDB.Reset Mocks
|
|
120
|
+
MockReq.Reset Mocks
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Mock Resource Keywords
|
|
124
|
+
|
|
125
|
+
Mock keywords from Robot Framework resource files:
|
|
126
|
+
|
|
127
|
+
```robot
|
|
128
|
+
*** Settings ***
|
|
129
|
+
Resource my_resource.robot
|
|
130
|
+
Library MockResource my_resource.robot WITH NAME MockRes
|
|
131
|
+
|
|
132
|
+
*** Test Cases ***
|
|
133
|
+
Test Resource Keyword Mock
|
|
134
|
+
MockRes.Mock Keyword My Custom Keyword return_value=mocked_value
|
|
135
|
+
${result}= My Custom Keyword
|
|
136
|
+
Should Be Equal ${result} mocked_value
|
|
137
|
+
MockRes.Reset Mocks
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Keywords
|
|
141
|
+
|
|
142
|
+
### Mock Keyword
|
|
143
|
+
|
|
144
|
+
Mock a keyword with a return value or side effect.
|
|
145
|
+
|
|
146
|
+
**Arguments:**
|
|
147
|
+
- `keyword_name` - Name of the keyword to mock
|
|
148
|
+
- `return_value` - Value to return when called (optional)
|
|
149
|
+
- `side_effect` - Callable to execute instead (optional)
|
|
150
|
+
|
|
151
|
+
**Example:**
|
|
152
|
+
```robot
|
|
153
|
+
MockDB.Mock Keyword query return_value=test_data
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Reset Mocks
|
|
157
|
+
|
|
158
|
+
Restore all mocked keywords to their original implementations.
|
|
159
|
+
|
|
160
|
+
**Example:**
|
|
161
|
+
```robot
|
|
162
|
+
MockDB.Reset Mocks
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Verify Keyword Called
|
|
166
|
+
|
|
167
|
+
Verify a keyword was called, optionally checking call count.
|
|
168
|
+
|
|
169
|
+
**Arguments:**
|
|
170
|
+
- `keyword_name` - Name of the keyword to verify
|
|
171
|
+
- `times` - Expected number of calls (optional)
|
|
172
|
+
|
|
173
|
+
**Example:**
|
|
174
|
+
```robot
|
|
175
|
+
MockDB.Verify Keyword Called execute_sql times=1
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## How It Works
|
|
179
|
+
|
|
180
|
+
### MockLibrary
|
|
181
|
+
|
|
182
|
+
MockLibrary dynamically replaces keyword implementations:
|
|
183
|
+
1. Wraps the target library instance
|
|
184
|
+
2. Resolves keyword names to function names (handles @keyword decorator)
|
|
185
|
+
3. Stores original methods before mocking
|
|
186
|
+
4. Replaces methods with mock implementations using Python's unittest.mock.Mock
|
|
187
|
+
5. Returns mocked values or executes side effects
|
|
188
|
+
6. Tracks call counts for verification
|
|
189
|
+
7. Raises AttributeError if attempting to mock a non-existent keyword
|
|
190
|
+
|
|
191
|
+
### MockResource
|
|
192
|
+
|
|
193
|
+
MockResource patches Robot Framework's keyword execution:
|
|
194
|
+
1. Patches the Namespace.get_runner method
|
|
195
|
+
2. Intercepts keyword execution for the specified resource file
|
|
196
|
+
3. Replaces keyword body with Return statement containing mocked value
|
|
197
|
+
4. Tracks call counts for verification
|
|
198
|
+
5. Restores original keyword body on reset
|
|
199
|
+
|
|
200
|
+
## Notes
|
|
201
|
+
|
|
202
|
+
- Both libraries use `ROBOT_LIBRARY_SCOPE = 'GLOBAL'` to maintain state across test cases
|
|
203
|
+
- Built on Python's unittest.mock.Mock for robust mocking capabilities
|
|
204
|
+
- MockLibrary supports any Robot Framework library, including BuiltIn
|
|
205
|
+
- MockResource works with resource files by patching the keyword execution pipeline
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
See LICENSE file for details.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
MockLibrary
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|