robotframework-mock 0.2.0__tar.gz → 0.3.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.
@@ -1,209 +1,209 @@
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
+ Metadata-Version: 2.4
2
+ Name: robotframework-mock
3
+ Version: 0.3.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,31 +1,31 @@
1
- [metadata]
2
- name = robotframework-mock
3
- version = 0.2.0
4
- author = Lajos Olah
5
- author_email = lajos.olah.jr@gmail.com
6
- description = A Robot Framework library for mocking keywords in unit tests
7
- long_description = file: README.md
8
- long_description_content_type = text/markdown
9
- url = https://github.com/olesz/robotframework-mock
10
- license = Apache-2.0
11
- classifiers =
12
- Programming Language :: Python :: 3
13
- Operating System :: OS Independent
14
- Framework :: Robot Framework
15
- Framework :: Robot Framework :: Library
16
-
17
- [options]
18
- package_dir =
19
- = src
20
- packages = find:
21
- python_requires = >=3.7
22
- install_requires =
23
- robotframework>=7.0
24
-
25
- [options.packages.find]
26
- where = src
27
-
28
- [egg_info]
29
- tag_build =
30
- tag_date = 0
31
-
1
+ [metadata]
2
+ name = robotframework-mock
3
+ version = 0.3.0
4
+ author = Lajos Olah
5
+ author_email = lajos.olah.jr@gmail.com
6
+ description = A Robot Framework library for mocking keywords in unit tests
7
+ long_description = file: README.md
8
+ long_description_content_type = text/markdown
9
+ url = https://github.com/olesz/robotframework-mock
10
+ license = Apache-2.0
11
+ classifiers =
12
+ Programming Language :: Python :: 3
13
+ Operating System :: OS Independent
14
+ Framework :: Robot Framework
15
+ Framework :: Robot Framework :: Library
16
+
17
+ [options]
18
+ package_dir =
19
+ = src
20
+ packages = find:
21
+ python_requires = >=3.7
22
+ install_requires =
23
+ robotframework>=7.0
24
+
25
+ [options.packages.find]
26
+ where = src
27
+
28
+ [egg_info]
29
+ tag_build =
30
+ tag_date = 0
31
+
@@ -1,6 +1,8 @@
1
1
  """Mock library for Robot Framework keyword mocking in unit tests."""
2
2
  # pylint: disable=invalid-name
3
+ import importlib.util
3
4
  import inspect
5
+ import os
4
6
  from typing import Any, Callable
5
7
  from unittest.mock import Mock
6
8
  from robot.api.deco import keyword
@@ -28,6 +30,49 @@ def _get_library_instance(library_name_or_alias):
28
30
  return lib
29
31
 
30
32
 
33
+ def _resolve_original_method(lib, method_name, keyword_name):
34
+ # Try direct attribute lookup first
35
+ try:
36
+ return getattr(lib, method_name), method_name
37
+ except AttributeError:
38
+ # If not found, search for methods with @keyword decorator custom names
39
+ for name, method in inspect.getmembers(
40
+ lib, inspect.ismethod
41
+ ):
42
+ if (hasattr(method, 'robot_name') and
43
+ method.robot_name.lower() == keyword_name):
44
+ return getattr(lib, name), name
45
+
46
+ return None, method_name
47
+
48
+
49
+ def _load_custom_resolver(resolver_path: str):
50
+ """Load a custom resolver from a Python file and instantiate it.
51
+
52
+ Args:
53
+ resolver_path: Relative path to a Python file containing a resolver class
54
+
55
+ Returns:
56
+ An instance of the resolver class found in the file
57
+
58
+ Raises:
59
+ FileNotFoundError: If the resolver file does not exist
60
+ AttributeError: If no class with resolve_original_method is found
61
+ """
62
+ abs_path = os.path.abspath(resolver_path)
63
+ if not os.path.isfile(abs_path):
64
+ raise FileNotFoundError(f"Custom resolver file not found: {abs_path}")
65
+ spec = importlib.util.spec_from_file_location("custom_resolver", abs_path)
66
+ module = importlib.util.module_from_spec(spec)
67
+ spec.loader.exec_module(module)
68
+ for _, obj in inspect.getmembers(module, inspect.isclass):
69
+ if hasattr(obj, 'resolve_original_method'):
70
+ return obj()
71
+ raise AttributeError(
72
+ f"No class with 'resolve_original_method' found in {abs_path}"
73
+ )
74
+
75
+
31
76
  class MockLibrary():
32
77
  """Mock keywords from any Robot Framework library for unit testing.
33
78
 
@@ -42,22 +87,22 @@ class MockLibrary():
42
87
 
43
88
  ROBOT_LIBRARY_SCOPE = 'GLOBAL'
44
89
 
45
- def __init__(self, library_name_or_alias: str, lib: Any = None):
90
+ def __init__(self, library_name_or_alias: str, custom_resolver_path: str = None):
46
91
  """Initialize MockLibrary with a target library to mock.
47
92
 
48
93
  Args:
49
94
  library_name_or_alias: Name or alias of the library to mock
50
- lib: Optional library instance (for testing purposes)
95
+ custom_resolver_path: Optional relative path to a Python file
96
+ containing a custom resolver class with a
97
+ resolve_original_method(lib, method_name, keyword_name) method
51
98
  """
52
- # Store original method references before mocking
53
99
  self._original_methods = {}
54
- # Store Mock objects for verification
55
100
  self._mocks = {}
56
- # Get or set the library instance to mock
57
- if lib:
58
- self._library_instance = lib
59
- else:
60
- self._library_instance = _get_library_instance(library_name_or_alias)
101
+ self._library_instance = _get_library_instance(library_name_or_alias)
102
+ self._custom_resolver = (
103
+ _load_custom_resolver(custom_resolver_path)
104
+ if custom_resolver_path else None
105
+ )
61
106
 
62
107
  @keyword
63
108
  def mock_keyword(
@@ -80,20 +125,17 @@ class MockLibrary():
80
125
 
81
126
  # Only store original method once per keyword
82
127
  if method_name not in self._original_methods:
83
- original_method = None
84
- # Try direct attribute lookup first
85
- try:
86
- original_method = getattr(lib, method_name)
87
- except AttributeError:
88
- # If not found, search for methods with @keyword decorator custom names
89
- for name, method in inspect.getmembers(
90
- lib, inspect.ismethod
91
- ):
92
- if (hasattr(method, 'robot_name') and
93
- method.robot_name.lower() == keyword_name):
94
- original_method = getattr(lib, name)
95
- method_name = name
96
- break
128
+ if self._custom_resolver:
129
+ original_method, method_name = (
130
+ self._custom_resolver.resolve_original_method(
131
+ lib, method_name, keyword_name, side_effect
132
+ )
133
+ )
134
+ else:
135
+ original_method, method_name = _resolve_original_method(
136
+ lib, method_name, keyword_name
137
+ )
138
+
97
139
  # Raise error if keyword doesn't exist
98
140
  if not original_method:
99
141
  raise AttributeError(f"Keyword '{keyword_name}' not found in {lib}")
@@ -1,113 +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
+ """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,209 +1,209 @@
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
+ Metadata-Version: 2.4
2
+ Name: robotframework-mock
3
+ Version: 0.3.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.