robotframework-mock 0.4.0__tar.gz → 0.6.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.4.0/src/robotframework_mock.egg-info → robotframework_mock-0.6.0}/PKG-INFO +77 -4
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/README.md +76 -3
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/setup.cfg +1 -1
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/MockLibrary/__init__.py +17 -1
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/MockResource/__init__.py +69 -5
- robotframework_mock-0.6.0/src/_mock_core/__init__.py +180 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0/src/robotframework_mock.egg-info}/PKG-INFO +77 -4
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/robotframework_mock.egg-info/SOURCES.txt +1 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/robotframework_mock.egg-info/top_level.txt +1 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/LICENSE +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/MANIFEST.in +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/pyproject.toml +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/setup.py +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/MockCoverage/__init__.py +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/robotframework_mock.egg-info/dependency_links.txt +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/robotframework_mock.egg-info/requires.txt +0 -0
{robotframework_mock-0.4.0/src/robotframework_mock.egg-info → robotframework_mock-0.6.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-mock
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -61,6 +61,7 @@ pip install -r requirements-dev.txt
|
|
|
61
61
|
- Mock Robot Framework's BuiltIn keywords
|
|
62
62
|
- Support for keywords with custom names via @keyword decorator
|
|
63
63
|
- Verify keyword calls and call counts
|
|
64
|
+
- Inspect and verify the arguments a mocked keyword was called with
|
|
64
65
|
- Measure resource-file keyword coverage with configurable thresholds
|
|
65
66
|
- Simple API with three main keywords
|
|
66
67
|
|
|
@@ -319,6 +320,14 @@ Mock a keyword with a return value or side effect.
|
|
|
319
320
|
MockDB.Mock Keyword query return_value=test_data
|
|
320
321
|
```
|
|
321
322
|
|
|
323
|
+
`return_value` may be any Python object, not only a string, so a keyword that
|
|
324
|
+
forwards a query result or a parsed response body can be mocked directly:
|
|
325
|
+
|
|
326
|
+
```robot
|
|
327
|
+
${rows}= Evaluate [['Europe/Budapest']]
|
|
328
|
+
MockRes.Mock Keyword Run Query return_value=${rows}
|
|
329
|
+
```
|
|
330
|
+
|
|
322
331
|
### Reset Mocks
|
|
323
332
|
|
|
324
333
|
Restore all mocked keywords to their original implementations.
|
|
@@ -341,6 +350,67 @@ Verify a keyword was called, optionally checking call count.
|
|
|
341
350
|
MockDB.Verify Keyword Called execute_sql times=1
|
|
342
351
|
```
|
|
343
352
|
|
|
353
|
+
### Verify Keyword Called With
|
|
354
|
+
|
|
355
|
+
Verify a keyword was called with the given arguments. Passes when **at least one**
|
|
356
|
+
recorded call matches, so the order of calls does not matter.
|
|
357
|
+
|
|
358
|
+
**Arguments:**
|
|
359
|
+
- `keyword_name` - Name of the keyword to verify
|
|
360
|
+
- `*args` - Expected positional arguments
|
|
361
|
+
- `**kwargs` - Expected named arguments
|
|
362
|
+
|
|
363
|
+
**Example:**
|
|
364
|
+
```robot
|
|
365
|
+
MockDB.Verify Keyword Called With execute_sql SELECT 1 timeout=${30}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Arguments are compared with Python equality, so types matter. Values are
|
|
369
|
+
recorded exactly as Robot Framework passed them to the keyword: test data is
|
|
370
|
+
string data, but Robot converts arguments of keywords that declare argument
|
|
371
|
+
types, and which built-in keywords declare types differs between Robot
|
|
372
|
+
Framework versions. When a recorded value is therefore not a string, give the
|
|
373
|
+
expectation as a typed Robot variable (`timeout=${30}` rather than
|
|
374
|
+
`timeout=30`), or read the call back with `Get Keyword Call Kwargs` and assert
|
|
375
|
+
on it with a type-insensitive comparison.
|
|
376
|
+
|
|
377
|
+
### Get Keyword Call Args
|
|
378
|
+
|
|
379
|
+
Return the positional arguments of one call to a mocked keyword.
|
|
380
|
+
|
|
381
|
+
**Arguments:**
|
|
382
|
+
- `keyword_name` - Name of the mocked keyword
|
|
383
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
384
|
+
|
|
385
|
+
**Example:**
|
|
386
|
+
```robot
|
|
387
|
+
${args}= MockDB.Get Keyword Call Args execute_sql index=0
|
|
388
|
+
Should Be Equal ${args}[0] SELECT 1
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Get Keyword Call Kwargs
|
|
392
|
+
|
|
393
|
+
Return the named arguments of one call to a mocked keyword.
|
|
394
|
+
|
|
395
|
+
**Arguments:**
|
|
396
|
+
- `keyword_name` - Name of the mocked keyword
|
|
397
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
398
|
+
|
|
399
|
+
**Example:**
|
|
400
|
+
```robot
|
|
401
|
+
${kwargs}= MockDB.Get Keyword Call Kwargs execute_sql
|
|
402
|
+
Should Be Equal ${kwargs}[timeout] ${30}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Get Keyword Call Count
|
|
406
|
+
|
|
407
|
+
Return how many times a mocked keyword was called.
|
|
408
|
+
|
|
409
|
+
**Example:**
|
|
410
|
+
```robot
|
|
411
|
+
${count}= MockDB.Get Keyword Call Count execute_sql
|
|
412
|
+
```
|
|
413
|
+
|
|
344
414
|
## How It Works
|
|
345
415
|
|
|
346
416
|
### MockLibrary
|
|
@@ -359,9 +429,12 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
359
429
|
MockResource patches Robot Framework's keyword execution:
|
|
360
430
|
1. Patches the Namespace.get_runner method
|
|
361
431
|
2. Intercepts keyword execution for the specified resource file
|
|
362
|
-
3.
|
|
363
|
-
|
|
364
|
-
|
|
432
|
+
3. Resolves the call's arguments - variables are replaced, and a `name=value`
|
|
433
|
+
argument becomes a named argument when the keyword declares that name - so
|
|
434
|
+
mocks record what the keyword was really called with
|
|
435
|
+
4. Replaces keyword body with Return statement containing mocked value
|
|
436
|
+
5. Tracks call counts and call arguments for verification
|
|
437
|
+
6. Restores original keyword body on reset
|
|
365
438
|
|
|
366
439
|
### MockCoverage
|
|
367
440
|
|
|
@@ -37,6 +37,7 @@ pip install -r requirements-dev.txt
|
|
|
37
37
|
- Mock Robot Framework's BuiltIn keywords
|
|
38
38
|
- Support for keywords with custom names via @keyword decorator
|
|
39
39
|
- Verify keyword calls and call counts
|
|
40
|
+
- Inspect and verify the arguments a mocked keyword was called with
|
|
40
41
|
- Measure resource-file keyword coverage with configurable thresholds
|
|
41
42
|
- Simple API with three main keywords
|
|
42
43
|
|
|
@@ -295,6 +296,14 @@ Mock a keyword with a return value or side effect.
|
|
|
295
296
|
MockDB.Mock Keyword query return_value=test_data
|
|
296
297
|
```
|
|
297
298
|
|
|
299
|
+
`return_value` may be any Python object, not only a string, so a keyword that
|
|
300
|
+
forwards a query result or a parsed response body can be mocked directly:
|
|
301
|
+
|
|
302
|
+
```robot
|
|
303
|
+
${rows}= Evaluate [['Europe/Budapest']]
|
|
304
|
+
MockRes.Mock Keyword Run Query return_value=${rows}
|
|
305
|
+
```
|
|
306
|
+
|
|
298
307
|
### Reset Mocks
|
|
299
308
|
|
|
300
309
|
Restore all mocked keywords to their original implementations.
|
|
@@ -317,6 +326,67 @@ Verify a keyword was called, optionally checking call count.
|
|
|
317
326
|
MockDB.Verify Keyword Called execute_sql times=1
|
|
318
327
|
```
|
|
319
328
|
|
|
329
|
+
### Verify Keyword Called With
|
|
330
|
+
|
|
331
|
+
Verify a keyword was called with the given arguments. Passes when **at least one**
|
|
332
|
+
recorded call matches, so the order of calls does not matter.
|
|
333
|
+
|
|
334
|
+
**Arguments:**
|
|
335
|
+
- `keyword_name` - Name of the keyword to verify
|
|
336
|
+
- `*args` - Expected positional arguments
|
|
337
|
+
- `**kwargs` - Expected named arguments
|
|
338
|
+
|
|
339
|
+
**Example:**
|
|
340
|
+
```robot
|
|
341
|
+
MockDB.Verify Keyword Called With execute_sql SELECT 1 timeout=${30}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Arguments are compared with Python equality, so types matter. Values are
|
|
345
|
+
recorded exactly as Robot Framework passed them to the keyword: test data is
|
|
346
|
+
string data, but Robot converts arguments of keywords that declare argument
|
|
347
|
+
types, and which built-in keywords declare types differs between Robot
|
|
348
|
+
Framework versions. When a recorded value is therefore not a string, give the
|
|
349
|
+
expectation as a typed Robot variable (`timeout=${30}` rather than
|
|
350
|
+
`timeout=30`), or read the call back with `Get Keyword Call Kwargs` and assert
|
|
351
|
+
on it with a type-insensitive comparison.
|
|
352
|
+
|
|
353
|
+
### Get Keyword Call Args
|
|
354
|
+
|
|
355
|
+
Return the positional arguments of one call to a mocked keyword.
|
|
356
|
+
|
|
357
|
+
**Arguments:**
|
|
358
|
+
- `keyword_name` - Name of the mocked keyword
|
|
359
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
360
|
+
|
|
361
|
+
**Example:**
|
|
362
|
+
```robot
|
|
363
|
+
${args}= MockDB.Get Keyword Call Args execute_sql index=0
|
|
364
|
+
Should Be Equal ${args}[0] SELECT 1
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Get Keyword Call Kwargs
|
|
368
|
+
|
|
369
|
+
Return the named arguments of one call to a mocked keyword.
|
|
370
|
+
|
|
371
|
+
**Arguments:**
|
|
372
|
+
- `keyword_name` - Name of the mocked keyword
|
|
373
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
374
|
+
|
|
375
|
+
**Example:**
|
|
376
|
+
```robot
|
|
377
|
+
${kwargs}= MockDB.Get Keyword Call Kwargs execute_sql
|
|
378
|
+
Should Be Equal ${kwargs}[timeout] ${30}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Get Keyword Call Count
|
|
382
|
+
|
|
383
|
+
Return how many times a mocked keyword was called.
|
|
384
|
+
|
|
385
|
+
**Example:**
|
|
386
|
+
```robot
|
|
387
|
+
${count}= MockDB.Get Keyword Call Count execute_sql
|
|
388
|
+
```
|
|
389
|
+
|
|
320
390
|
## How It Works
|
|
321
391
|
|
|
322
392
|
### MockLibrary
|
|
@@ -335,9 +405,12 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
335
405
|
MockResource patches Robot Framework's keyword execution:
|
|
336
406
|
1. Patches the Namespace.get_runner method
|
|
337
407
|
2. Intercepts keyword execution for the specified resource file
|
|
338
|
-
3.
|
|
339
|
-
|
|
340
|
-
|
|
408
|
+
3. Resolves the call's arguments - variables are replaced, and a `name=value`
|
|
409
|
+
argument becomes a named argument when the keyword declares that name - so
|
|
410
|
+
mocks record what the keyword was really called with
|
|
411
|
+
4. Replaces keyword body with Return statement containing mocked value
|
|
412
|
+
5. Tracks call counts and call arguments for verification
|
|
413
|
+
6. Restores original keyword body on reset
|
|
341
414
|
|
|
342
415
|
### MockCoverage
|
|
343
416
|
|
|
@@ -8,6 +8,8 @@ from unittest.mock import Mock
|
|
|
8
8
|
from robot.api.deco import keyword
|
|
9
9
|
from robot.libraries.BuiltIn import BuiltIn
|
|
10
10
|
|
|
11
|
+
from _mock_core import CallInspectionMixin
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
def _get_library_instance(library_name_or_alias):
|
|
13
15
|
"""Retrieve a library instance from Robot Framework's runtime.
|
|
@@ -73,7 +75,7 @@ def _load_custom_resolver(resolver_path: str):
|
|
|
73
75
|
)
|
|
74
76
|
|
|
75
77
|
|
|
76
|
-
class MockLibrary():
|
|
78
|
+
class MockLibrary(CallInspectionMixin):
|
|
77
79
|
"""Mock keywords from any Robot Framework library for unit testing.
|
|
78
80
|
|
|
79
81
|
Example:
|
|
@@ -87,6 +89,20 @@ class MockLibrary():
|
|
|
87
89
|
|
|
88
90
|
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
|
|
89
91
|
|
|
92
|
+
def _mock_key(self, keyword_name: str) -> str:
|
|
93
|
+
"""Return the ``self._mocks`` key for *keyword_name*.
|
|
94
|
+
|
|
95
|
+
Mocks are stored under the method name, so the keyword name is
|
|
96
|
+
normalised the same way :meth:`mock_keyword` normalises it.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
keyword_name: Keyword name as written in the test.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The method name used as the mock key.
|
|
103
|
+
"""
|
|
104
|
+
return keyword_name.lower().replace(' ', '_')
|
|
105
|
+
|
|
90
106
|
def __init__(self, library_name_or_alias: str, custom_resolver_path: str = None):
|
|
91
107
|
"""Initialize MockLibrary with a target library to mock.
|
|
92
108
|
|
|
@@ -7,9 +7,60 @@ from robot.api.deco import keyword
|
|
|
7
7
|
from robot.libraries.BuiltIn import BuiltIn
|
|
8
8
|
from robot.running import Return
|
|
9
9
|
from robot.running.namespace import Namespace
|
|
10
|
+
from robot.utils import split_from_equals
|
|
10
11
|
|
|
12
|
+
from _mock_core import CallInspectionMixin
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
# Robot resolves the values of a RETURN statement as variable expressions, so a
|
|
15
|
+
# mocked return value cannot be embedded in one directly - anything that is not
|
|
16
|
+
# a string raises a TypeError. The value is therefore published under this name
|
|
17
|
+
# and the injected RETURN refers to it, which lets a mock return any Python
|
|
18
|
+
# object (a query result set, a parsed JSON body, a list of pods...).
|
|
19
|
+
RETURN_VARIABLE = '${__mock_resource_return__}'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _resolve_arguments(keyword_obj, data, context):
|
|
23
|
+
"""Split a keyword call's raw arguments into resolved args and kwargs.
|
|
24
|
+
|
|
25
|
+
Robot hands the listener the argument list exactly as written at the call
|
|
26
|
+
site, so variables are still unresolved (``${query} commit;``) and named
|
|
27
|
+
arguments are plain ``name=value`` strings. Both are resolved here so that
|
|
28
|
+
mocks record what the keyword was *really* called with, matching what
|
|
29
|
+
``MockLibrary`` records for library keywords.
|
|
30
|
+
|
|
31
|
+
A ``name=value`` item only becomes a named argument when ``name`` is an
|
|
32
|
+
argument the keyword actually declares; otherwise it stays positional, so a
|
|
33
|
+
value that merely contains ``=`` is not misread.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
keyword_obj: The keyword being called, used for its argument spec.
|
|
37
|
+
data: Robot keyword call data holding the raw arguments.
|
|
38
|
+
context: Robot execution context, used to resolve variables.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
A ``(args, kwargs)`` tuple of resolved arguments.
|
|
42
|
+
"""
|
|
43
|
+
spec = getattr(keyword_obj, 'args', None)
|
|
44
|
+
accepted = set()
|
|
45
|
+
if spec is not None:
|
|
46
|
+
accepted.update(spec.positional or ())
|
|
47
|
+
accepted.update(spec.named_only or ())
|
|
48
|
+
|
|
49
|
+
args = []
|
|
50
|
+
kwargs = {}
|
|
51
|
+
for argument in context.variables.replace_list(data.args):
|
|
52
|
+
name, value = (
|
|
53
|
+
split_from_equals(argument) if isinstance(argument, str)
|
|
54
|
+
else (None, None)
|
|
55
|
+
)
|
|
56
|
+
if value is not None and name in accepted:
|
|
57
|
+
kwargs[name] = value
|
|
58
|
+
else:
|
|
59
|
+
args.append(argument)
|
|
60
|
+
return args, kwargs
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class MockResource(CallInspectionMixin):
|
|
13
64
|
"""Mock keywords from Robot Framework resource files for unit testing.
|
|
14
65
|
|
|
15
66
|
Example:
|
|
@@ -42,8 +93,15 @@ class MockResource:
|
|
|
42
93
|
if mock:
|
|
43
94
|
original_run = keyword_runner.run
|
|
44
95
|
def patched_run(data, result, context, run):
|
|
45
|
-
|
|
46
|
-
|
|
96
|
+
args, kwargs = _resolve_arguments(
|
|
97
|
+
keyword_runner.keyword, data, context
|
|
98
|
+
)
|
|
99
|
+
mock_result = mock(*args, **kwargs)
|
|
100
|
+
# Test scope, so the value is visible to the keyword's own
|
|
101
|
+
# body even when the mocked keyword is called from inside
|
|
102
|
+
# another keyword rather than directly from the test.
|
|
103
|
+
context.variables.set_test(RETURN_VARIABLE, mock_result)
|
|
104
|
+
keyword_runner.keyword.body._items = [Return(values=[RETURN_VARIABLE])] # pylint: disable=protected-access
|
|
47
105
|
return original_run(data, result, context, run)
|
|
48
106
|
keyword_runner.run = patched_run
|
|
49
107
|
|
|
@@ -61,8 +119,13 @@ class MockResource:
|
|
|
61
119
|
Args:
|
|
62
120
|
keyword_name: Name of the keyword to mock
|
|
63
121
|
return_value: Value to return when the keyword is called
|
|
64
|
-
side_effect: Callable to execute instead of returning a value
|
|
65
|
-
|
|
122
|
+
side_effect: Callable to execute instead of returning a value.
|
|
123
|
+
Receives the call's resolved arguments, the same way a
|
|
124
|
+
``MockLibrary`` side effect does.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
The :class:`unittest.mock.Mock` backing the keyword.
|
|
128
|
+
|
|
66
129
|
Example:
|
|
67
130
|
| MockRes.Mock Keyword | My Keyword | return_value=test_data |
|
|
68
131
|
"""
|
|
@@ -75,6 +138,7 @@ class MockResource:
|
|
|
75
138
|
self._original_items[keyword_name] = keyword_runner.keyword.body._items # pylint: disable=protected-access
|
|
76
139
|
mock = Mock(return_value=return_value, side_effect=side_effect)
|
|
77
140
|
self._mocks[keyword_name] = mock
|
|
141
|
+
return mock
|
|
78
142
|
|
|
79
143
|
@keyword
|
|
80
144
|
def reset_mocks(self):
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Shared internals for the mock libraries.
|
|
2
|
+
|
|
3
|
+
Private package - not part of the public API, and not meant to be imported by
|
|
4
|
+
tests. ``MockLibrary`` and ``MockResource`` both keep their mocked keywords in a
|
|
5
|
+
``self._mocks`` mapping of :class:`unittest.mock.Mock` objects, which already
|
|
6
|
+
record every call. The keywords that expose those recorded calls are therefore
|
|
7
|
+
implemented once here and mixed into both libraries, so the two cannot drift
|
|
8
|
+
apart.
|
|
9
|
+
"""
|
|
10
|
+
# pylint: disable=invalid-name
|
|
11
|
+
from typing import Any, Dict, List
|
|
12
|
+
from unittest.mock import call
|
|
13
|
+
|
|
14
|
+
from robot.api.deco import keyword
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CallInspectionMixin:
|
|
18
|
+
"""Keywords for inspecting how a mocked keyword was called.
|
|
19
|
+
|
|
20
|
+
The host library must provide:
|
|
21
|
+
|
|
22
|
+
- ``self._mocks``: mapping of mock key -> :class:`unittest.mock.Mock`
|
|
23
|
+
- ``self._mock_key(keyword_name)``: the ``self._mocks`` key for a keyword
|
|
24
|
+
name, in case the library does not key by the name as written
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
_mocks: Dict[str, Any]
|
|
28
|
+
|
|
29
|
+
def _mock_key(self, keyword_name: str) -> str:
|
|
30
|
+
"""Return the ``self._mocks`` key for *keyword_name*.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
keyword_name: Keyword name as written in the test.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
The key under which the mock is stored. Libraries that normalise
|
|
37
|
+
keyword names override this.
|
|
38
|
+
"""
|
|
39
|
+
return keyword_name
|
|
40
|
+
|
|
41
|
+
def _get_mock(self, keyword_name: str):
|
|
42
|
+
"""Return the mock for *keyword_name*.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
keyword_name: Name of the mocked keyword.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
The :class:`unittest.mock.Mock` backing that keyword.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
AssertionError: If the keyword was not mocked.
|
|
52
|
+
"""
|
|
53
|
+
key = self._mock_key(keyword_name)
|
|
54
|
+
if key not in self._mocks:
|
|
55
|
+
raise AssertionError(f"Keyword '{keyword_name}' was not mocked")
|
|
56
|
+
return self._mocks[key]
|
|
57
|
+
|
|
58
|
+
def _get_call(self, keyword_name: str, index):
|
|
59
|
+
"""Return a single recorded call.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
keyword_name: Name of the mocked keyword.
|
|
63
|
+
index: Zero-based call index. Negative values count from the end,
|
|
64
|
+
so ``-1`` is the most recent call.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The :class:`unittest.mock.call` recorded at that index.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
AssertionError: If the keyword was not mocked, or fewer calls were
|
|
71
|
+
recorded than *index* requires.
|
|
72
|
+
"""
|
|
73
|
+
calls = self._get_mock(keyword_name).call_args_list
|
|
74
|
+
index = int(index)
|
|
75
|
+
if not -len(calls) <= index < len(calls):
|
|
76
|
+
raise AssertionError(
|
|
77
|
+
f"No call recorded at index {index} for keyword "
|
|
78
|
+
f"'{keyword_name}', it was called {len(calls)} time(s)"
|
|
79
|
+
)
|
|
80
|
+
return calls[index]
|
|
81
|
+
|
|
82
|
+
@keyword
|
|
83
|
+
def get_keyword_call_count(self, keyword_name: str) -> int:
|
|
84
|
+
"""Return how many times a mocked keyword was called.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
keyword_name: Name of the mocked keyword.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The number of recorded calls.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
AssertionError: If the keyword was not mocked.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
| ${count}= | MockDB.Get Keyword Call Count | Execute Sql |
|
|
97
|
+
"""
|
|
98
|
+
return self._get_mock(keyword_name).call_count
|
|
99
|
+
|
|
100
|
+
@keyword
|
|
101
|
+
def get_keyword_call_args(self, keyword_name: str, index=0) -> List[Any]:
|
|
102
|
+
"""Return the positional arguments of one call to a mocked keyword.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
keyword_name: Name of the mocked keyword.
|
|
106
|
+
index: Zero-based call index, negative counts from the end.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
The positional arguments of that call.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
AssertionError: If the keyword was not mocked or the call does not
|
|
113
|
+
exist.
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
| ${args}= | MockDB.Get Keyword Call Args | Execute Sql | index=0 |
|
|
117
|
+
| Should Be Equal | ${args}[0] | SELECT 1 |
|
|
118
|
+
"""
|
|
119
|
+
return list(self._get_call(keyword_name, index).args)
|
|
120
|
+
|
|
121
|
+
@keyword
|
|
122
|
+
def get_keyword_call_kwargs(self, keyword_name: str, index=0) -> Dict[str, Any]:
|
|
123
|
+
"""Return the named arguments of one call to a mocked keyword.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
keyword_name: Name of the mocked keyword.
|
|
127
|
+
index: Zero-based call index, negative counts from the end.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
The named arguments of that call.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
AssertionError: If the keyword was not mocked or the call does not
|
|
134
|
+
exist.
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
| ${kwargs}= | MockDB.Get Keyword Call Kwargs | Execute Sql |
|
|
138
|
+
| Should Be Equal | ${kwargs}[timeout] | 30 |
|
|
139
|
+
"""
|
|
140
|
+
return dict(self._get_call(keyword_name, index).kwargs)
|
|
141
|
+
|
|
142
|
+
@keyword
|
|
143
|
+
def verify_keyword_called_with(self, keyword_name: str, *args, **kwargs):
|
|
144
|
+
"""Verify a mocked keyword was called with the given arguments.
|
|
145
|
+
|
|
146
|
+
Passes when **at least one** recorded call matches exactly, which keeps
|
|
147
|
+
the check usable when the order of calls is not significant. Use
|
|
148
|
+
:meth:`get_keyword_call_args` to assert on a specific call instead.
|
|
149
|
+
|
|
150
|
+
Arguments are compared with Python equality, so types matter. Values
|
|
151
|
+
are recorded exactly as Robot Framework passed them to the keyword, and
|
|
152
|
+
Robot converts arguments of keywords that declare argument types. Which
|
|
153
|
+
built-in keywords declare types differs between Robot Framework
|
|
154
|
+
versions, so a non-string expectation must be given as a typed Robot
|
|
155
|
+
variable (``timeout=${30}`` rather than ``timeout=30``).
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
keyword_name: Name of the mocked keyword.
|
|
159
|
+
*args: Expected positional arguments.
|
|
160
|
+
**kwargs: Expected named arguments.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
AssertionError: If the keyword was not mocked, or no recorded call
|
|
164
|
+
matches.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
| MockDB.Verify Keyword Called With | Execute Sql | SELECT 1 |
|
|
168
|
+
"""
|
|
169
|
+
mock = self._get_mock(keyword_name)
|
|
170
|
+
expected = call(*args, **kwargs)
|
|
171
|
+
if expected not in mock.call_args_list:
|
|
172
|
+
actual = (
|
|
173
|
+
'\n'.join(f' {index}: {item}'
|
|
174
|
+
for index, item in enumerate(mock.call_args_list))
|
|
175
|
+
or ' (never called)'
|
|
176
|
+
)
|
|
177
|
+
raise AssertionError(
|
|
178
|
+
f"Keyword '{keyword_name}' was not called with {expected}.\n"
|
|
179
|
+
f"Recorded calls:\n{actual}"
|
|
180
|
+
)
|
{robotframework_mock-0.4.0 → robotframework_mock-0.6.0/src/robotframework_mock.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-mock
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -61,6 +61,7 @@ pip install -r requirements-dev.txt
|
|
|
61
61
|
- Mock Robot Framework's BuiltIn keywords
|
|
62
62
|
- Support for keywords with custom names via @keyword decorator
|
|
63
63
|
- Verify keyword calls and call counts
|
|
64
|
+
- Inspect and verify the arguments a mocked keyword was called with
|
|
64
65
|
- Measure resource-file keyword coverage with configurable thresholds
|
|
65
66
|
- Simple API with three main keywords
|
|
66
67
|
|
|
@@ -319,6 +320,14 @@ Mock a keyword with a return value or side effect.
|
|
|
319
320
|
MockDB.Mock Keyword query return_value=test_data
|
|
320
321
|
```
|
|
321
322
|
|
|
323
|
+
`return_value` may be any Python object, not only a string, so a keyword that
|
|
324
|
+
forwards a query result or a parsed response body can be mocked directly:
|
|
325
|
+
|
|
326
|
+
```robot
|
|
327
|
+
${rows}= Evaluate [['Europe/Budapest']]
|
|
328
|
+
MockRes.Mock Keyword Run Query return_value=${rows}
|
|
329
|
+
```
|
|
330
|
+
|
|
322
331
|
### Reset Mocks
|
|
323
332
|
|
|
324
333
|
Restore all mocked keywords to their original implementations.
|
|
@@ -341,6 +350,67 @@ Verify a keyword was called, optionally checking call count.
|
|
|
341
350
|
MockDB.Verify Keyword Called execute_sql times=1
|
|
342
351
|
```
|
|
343
352
|
|
|
353
|
+
### Verify Keyword Called With
|
|
354
|
+
|
|
355
|
+
Verify a keyword was called with the given arguments. Passes when **at least one**
|
|
356
|
+
recorded call matches, so the order of calls does not matter.
|
|
357
|
+
|
|
358
|
+
**Arguments:**
|
|
359
|
+
- `keyword_name` - Name of the keyword to verify
|
|
360
|
+
- `*args` - Expected positional arguments
|
|
361
|
+
- `**kwargs` - Expected named arguments
|
|
362
|
+
|
|
363
|
+
**Example:**
|
|
364
|
+
```robot
|
|
365
|
+
MockDB.Verify Keyword Called With execute_sql SELECT 1 timeout=${30}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Arguments are compared with Python equality, so types matter. Values are
|
|
369
|
+
recorded exactly as Robot Framework passed them to the keyword: test data is
|
|
370
|
+
string data, but Robot converts arguments of keywords that declare argument
|
|
371
|
+
types, and which built-in keywords declare types differs between Robot
|
|
372
|
+
Framework versions. When a recorded value is therefore not a string, give the
|
|
373
|
+
expectation as a typed Robot variable (`timeout=${30}` rather than
|
|
374
|
+
`timeout=30`), or read the call back with `Get Keyword Call Kwargs` and assert
|
|
375
|
+
on it with a type-insensitive comparison.
|
|
376
|
+
|
|
377
|
+
### Get Keyword Call Args
|
|
378
|
+
|
|
379
|
+
Return the positional arguments of one call to a mocked keyword.
|
|
380
|
+
|
|
381
|
+
**Arguments:**
|
|
382
|
+
- `keyword_name` - Name of the mocked keyword
|
|
383
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
384
|
+
|
|
385
|
+
**Example:**
|
|
386
|
+
```robot
|
|
387
|
+
${args}= MockDB.Get Keyword Call Args execute_sql index=0
|
|
388
|
+
Should Be Equal ${args}[0] SELECT 1
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Get Keyword Call Kwargs
|
|
392
|
+
|
|
393
|
+
Return the named arguments of one call to a mocked keyword.
|
|
394
|
+
|
|
395
|
+
**Arguments:**
|
|
396
|
+
- `keyword_name` - Name of the mocked keyword
|
|
397
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
398
|
+
|
|
399
|
+
**Example:**
|
|
400
|
+
```robot
|
|
401
|
+
${kwargs}= MockDB.Get Keyword Call Kwargs execute_sql
|
|
402
|
+
Should Be Equal ${kwargs}[timeout] ${30}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Get Keyword Call Count
|
|
406
|
+
|
|
407
|
+
Return how many times a mocked keyword was called.
|
|
408
|
+
|
|
409
|
+
**Example:**
|
|
410
|
+
```robot
|
|
411
|
+
${count}= MockDB.Get Keyword Call Count execute_sql
|
|
412
|
+
```
|
|
413
|
+
|
|
344
414
|
## How It Works
|
|
345
415
|
|
|
346
416
|
### MockLibrary
|
|
@@ -359,9 +429,12 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
359
429
|
MockResource patches Robot Framework's keyword execution:
|
|
360
430
|
1. Patches the Namespace.get_runner method
|
|
361
431
|
2. Intercepts keyword execution for the specified resource file
|
|
362
|
-
3.
|
|
363
|
-
|
|
364
|
-
|
|
432
|
+
3. Resolves the call's arguments - variables are replaced, and a `name=value`
|
|
433
|
+
argument becomes a named argument when the keyword declares that name - so
|
|
434
|
+
mocks record what the keyword was really called with
|
|
435
|
+
4. Replaces keyword body with Return statement containing mocked value
|
|
436
|
+
5. Tracks call counts and call arguments for verification
|
|
437
|
+
6. Restores original keyword body on reset
|
|
365
438
|
|
|
366
439
|
### MockCoverage
|
|
367
440
|
|
{robotframework_mock-0.4.0 → robotframework_mock-0.6.0}/src/robotframework_mock.egg-info/SOURCES.txt
RENAMED
|
@@ -7,6 +7,7 @@ setup.py
|
|
|
7
7
|
src/MockCoverage/__init__.py
|
|
8
8
|
src/MockLibrary/__init__.py
|
|
9
9
|
src/MockResource/__init__.py
|
|
10
|
+
src/_mock_core/__init__.py
|
|
10
11
|
src/robotframework_mock.egg-info/PKG-INFO
|
|
11
12
|
src/robotframework_mock.egg-info/SOURCES.txt
|
|
12
13
|
src/robotframework_mock.egg-info/dependency_links.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|