robotframework-mock 0.4.0__tar.gz → 0.5.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.5.0}/PKG-INFO +69 -4
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/README.md +68 -3
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/setup.cfg +1 -1
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/MockLibrary/__init__.py +17 -1
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/MockResource/__init__.py +57 -4
- robotframework_mock-0.5.0/src/_mock_core/__init__.py +180 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0/src/robotframework_mock.egg-info}/PKG-INFO +69 -4
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/robotframework_mock.egg-info/SOURCES.txt +1 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/robotframework_mock.egg-info/top_level.txt +1 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/LICENSE +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/MANIFEST.in +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/pyproject.toml +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/setup.py +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/MockCoverage/__init__.py +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/robotframework_mock.egg-info/dependency_links.txt +0 -0
- {robotframework_mock-0.4.0 → robotframework_mock-0.5.0}/src/robotframework_mock.egg-info/requires.txt +0 -0
{robotframework_mock-0.4.0/src/robotframework_mock.egg-info → robotframework_mock-0.5.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.5.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
|
|
|
@@ -341,6 +342,67 @@ Verify a keyword was called, optionally checking call count.
|
|
|
341
342
|
MockDB.Verify Keyword Called execute_sql times=1
|
|
342
343
|
```
|
|
343
344
|
|
|
345
|
+
### Verify Keyword Called With
|
|
346
|
+
|
|
347
|
+
Verify a keyword was called with the given arguments. Passes when **at least one**
|
|
348
|
+
recorded call matches, so the order of calls does not matter.
|
|
349
|
+
|
|
350
|
+
**Arguments:**
|
|
351
|
+
- `keyword_name` - Name of the keyword to verify
|
|
352
|
+
- `*args` - Expected positional arguments
|
|
353
|
+
- `**kwargs` - Expected named arguments
|
|
354
|
+
|
|
355
|
+
**Example:**
|
|
356
|
+
```robot
|
|
357
|
+
MockDB.Verify Keyword Called With execute_sql SELECT 1 timeout=${30}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Arguments are compared with Python equality, so types matter. Values are
|
|
361
|
+
recorded exactly as Robot Framework passed them to the keyword: test data is
|
|
362
|
+
string data, but Robot converts arguments of keywords that declare argument
|
|
363
|
+
types, and which built-in keywords declare types differs between Robot
|
|
364
|
+
Framework versions. When a recorded value is therefore not a string, give the
|
|
365
|
+
expectation as a typed Robot variable (`timeout=${30}` rather than
|
|
366
|
+
`timeout=30`), or read the call back with `Get Keyword Call Kwargs` and assert
|
|
367
|
+
on it with a type-insensitive comparison.
|
|
368
|
+
|
|
369
|
+
### Get Keyword Call Args
|
|
370
|
+
|
|
371
|
+
Return the positional arguments of one call to a mocked keyword.
|
|
372
|
+
|
|
373
|
+
**Arguments:**
|
|
374
|
+
- `keyword_name` - Name of the mocked keyword
|
|
375
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
376
|
+
|
|
377
|
+
**Example:**
|
|
378
|
+
```robot
|
|
379
|
+
${args}= MockDB.Get Keyword Call Args execute_sql index=0
|
|
380
|
+
Should Be Equal ${args}[0] SELECT 1
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Get Keyword Call Kwargs
|
|
384
|
+
|
|
385
|
+
Return the named arguments of one call to a mocked keyword.
|
|
386
|
+
|
|
387
|
+
**Arguments:**
|
|
388
|
+
- `keyword_name` - Name of the mocked keyword
|
|
389
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
390
|
+
|
|
391
|
+
**Example:**
|
|
392
|
+
```robot
|
|
393
|
+
${kwargs}= MockDB.Get Keyword Call Kwargs execute_sql
|
|
394
|
+
Should Be Equal ${kwargs}[timeout] ${30}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Get Keyword Call Count
|
|
398
|
+
|
|
399
|
+
Return how many times a mocked keyword was called.
|
|
400
|
+
|
|
401
|
+
**Example:**
|
|
402
|
+
```robot
|
|
403
|
+
${count}= MockDB.Get Keyword Call Count execute_sql
|
|
404
|
+
```
|
|
405
|
+
|
|
344
406
|
## How It Works
|
|
345
407
|
|
|
346
408
|
### MockLibrary
|
|
@@ -359,9 +421,12 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
359
421
|
MockResource patches Robot Framework's keyword execution:
|
|
360
422
|
1. Patches the Namespace.get_runner method
|
|
361
423
|
2. Intercepts keyword execution for the specified resource file
|
|
362
|
-
3.
|
|
363
|
-
|
|
364
|
-
|
|
424
|
+
3. Resolves the call's arguments - variables are replaced, and a `name=value`
|
|
425
|
+
argument becomes a named argument when the keyword declares that name - so
|
|
426
|
+
mocks record what the keyword was really called with
|
|
427
|
+
4. Replaces keyword body with Return statement containing mocked value
|
|
428
|
+
5. Tracks call counts and call arguments for verification
|
|
429
|
+
6. Restores original keyword body on reset
|
|
365
430
|
|
|
366
431
|
### MockCoverage
|
|
367
432
|
|
|
@@ -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
|
|
|
@@ -317,6 +318,67 @@ Verify a keyword was called, optionally checking call count.
|
|
|
317
318
|
MockDB.Verify Keyword Called execute_sql times=1
|
|
318
319
|
```
|
|
319
320
|
|
|
321
|
+
### Verify Keyword Called With
|
|
322
|
+
|
|
323
|
+
Verify a keyword was called with the given arguments. Passes when **at least one**
|
|
324
|
+
recorded call matches, so the order of calls does not matter.
|
|
325
|
+
|
|
326
|
+
**Arguments:**
|
|
327
|
+
- `keyword_name` - Name of the keyword to verify
|
|
328
|
+
- `*args` - Expected positional arguments
|
|
329
|
+
- `**kwargs` - Expected named arguments
|
|
330
|
+
|
|
331
|
+
**Example:**
|
|
332
|
+
```robot
|
|
333
|
+
MockDB.Verify Keyword Called With execute_sql SELECT 1 timeout=${30}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Arguments are compared with Python equality, so types matter. Values are
|
|
337
|
+
recorded exactly as Robot Framework passed them to the keyword: test data is
|
|
338
|
+
string data, but Robot converts arguments of keywords that declare argument
|
|
339
|
+
types, and which built-in keywords declare types differs between Robot
|
|
340
|
+
Framework versions. When a recorded value is therefore not a string, give the
|
|
341
|
+
expectation as a typed Robot variable (`timeout=${30}` rather than
|
|
342
|
+
`timeout=30`), or read the call back with `Get Keyword Call Kwargs` and assert
|
|
343
|
+
on it with a type-insensitive comparison.
|
|
344
|
+
|
|
345
|
+
### Get Keyword Call Args
|
|
346
|
+
|
|
347
|
+
Return the positional arguments of one call to a mocked keyword.
|
|
348
|
+
|
|
349
|
+
**Arguments:**
|
|
350
|
+
- `keyword_name` - Name of the mocked keyword
|
|
351
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
352
|
+
|
|
353
|
+
**Example:**
|
|
354
|
+
```robot
|
|
355
|
+
${args}= MockDB.Get Keyword Call Args execute_sql index=0
|
|
356
|
+
Should Be Equal ${args}[0] SELECT 1
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Get Keyword Call Kwargs
|
|
360
|
+
|
|
361
|
+
Return the named arguments of one call to a mocked keyword.
|
|
362
|
+
|
|
363
|
+
**Arguments:**
|
|
364
|
+
- `keyword_name` - Name of the mocked keyword
|
|
365
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
366
|
+
|
|
367
|
+
**Example:**
|
|
368
|
+
```robot
|
|
369
|
+
${kwargs}= MockDB.Get Keyword Call Kwargs execute_sql
|
|
370
|
+
Should Be Equal ${kwargs}[timeout] ${30}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Get Keyword Call Count
|
|
374
|
+
|
|
375
|
+
Return how many times a mocked keyword was called.
|
|
376
|
+
|
|
377
|
+
**Example:**
|
|
378
|
+
```robot
|
|
379
|
+
${count}= MockDB.Get Keyword Call Count execute_sql
|
|
380
|
+
```
|
|
381
|
+
|
|
320
382
|
## How It Works
|
|
321
383
|
|
|
322
384
|
### MockLibrary
|
|
@@ -335,9 +397,12 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
335
397
|
MockResource patches Robot Framework's keyword execution:
|
|
336
398
|
1. Patches the Namespace.get_runner method
|
|
337
399
|
2. Intercepts keyword execution for the specified resource file
|
|
338
|
-
3.
|
|
339
|
-
|
|
340
|
-
|
|
400
|
+
3. Resolves the call's arguments - variables are replaced, and a `name=value`
|
|
401
|
+
argument becomes a named argument when the keyword declares that name - so
|
|
402
|
+
mocks record what the keyword was really called with
|
|
403
|
+
4. Replaces keyword body with Return statement containing mocked value
|
|
404
|
+
5. Tracks call counts and call arguments for verification
|
|
405
|
+
6. Restores original keyword body on reset
|
|
341
406
|
|
|
342
407
|
### MockCoverage
|
|
343
408
|
|
|
@@ -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,53 @@ 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
|
+
|
|
15
|
+
def _resolve_arguments(keyword_obj, data, context):
|
|
16
|
+
"""Split a keyword call's raw arguments into resolved args and kwargs.
|
|
17
|
+
|
|
18
|
+
Robot hands the listener the argument list exactly as written at the call
|
|
19
|
+
site, so variables are still unresolved (``${query} commit;``) and named
|
|
20
|
+
arguments are plain ``name=value`` strings. Both are resolved here so that
|
|
21
|
+
mocks record what the keyword was *really* called with, matching what
|
|
22
|
+
``MockLibrary`` records for library keywords.
|
|
23
|
+
|
|
24
|
+
A ``name=value`` item only becomes a named argument when ``name`` is an
|
|
25
|
+
argument the keyword actually declares; otherwise it stays positional, so a
|
|
26
|
+
value that merely contains ``=`` is not misread.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
keyword_obj: The keyword being called, used for its argument spec.
|
|
30
|
+
data: Robot keyword call data holding the raw arguments.
|
|
31
|
+
context: Robot execution context, used to resolve variables.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
A ``(args, kwargs)`` tuple of resolved arguments.
|
|
35
|
+
"""
|
|
36
|
+
spec = getattr(keyword_obj, 'args', None)
|
|
37
|
+
accepted = set()
|
|
38
|
+
if spec is not None:
|
|
39
|
+
accepted.update(spec.positional or ())
|
|
40
|
+
accepted.update(spec.named_only or ())
|
|
41
|
+
|
|
42
|
+
args = []
|
|
43
|
+
kwargs = {}
|
|
44
|
+
for argument in context.variables.replace_list(data.args):
|
|
45
|
+
name, value = (
|
|
46
|
+
split_from_equals(argument) if isinstance(argument, str)
|
|
47
|
+
else (None, None)
|
|
48
|
+
)
|
|
49
|
+
if value is not None and name in accepted:
|
|
50
|
+
kwargs[name] = value
|
|
51
|
+
else:
|
|
52
|
+
args.append(argument)
|
|
53
|
+
return args, kwargs
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MockResource(CallInspectionMixin):
|
|
13
57
|
"""Mock keywords from Robot Framework resource files for unit testing.
|
|
14
58
|
|
|
15
59
|
Example:
|
|
@@ -42,7 +86,10 @@ class MockResource:
|
|
|
42
86
|
if mock:
|
|
43
87
|
original_run = keyword_runner.run
|
|
44
88
|
def patched_run(data, result, context, run):
|
|
45
|
-
|
|
89
|
+
args, kwargs = _resolve_arguments(
|
|
90
|
+
keyword_runner.keyword, data, context
|
|
91
|
+
)
|
|
92
|
+
mock_result = mock(*args, **kwargs)
|
|
46
93
|
keyword_runner.keyword.body._items = [Return(values=[mock_result])] # pylint: disable=protected-access
|
|
47
94
|
return original_run(data, result, context, run)
|
|
48
95
|
keyword_runner.run = patched_run
|
|
@@ -61,8 +108,13 @@ class MockResource:
|
|
|
61
108
|
Args:
|
|
62
109
|
keyword_name: Name of the keyword to mock
|
|
63
110
|
return_value: Value to return when the keyword is called
|
|
64
|
-
side_effect: Callable to execute instead of returning a value
|
|
65
|
-
|
|
111
|
+
side_effect: Callable to execute instead of returning a value.
|
|
112
|
+
Receives the call's resolved arguments, the same way a
|
|
113
|
+
``MockLibrary`` side effect does.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
The :class:`unittest.mock.Mock` backing the keyword.
|
|
117
|
+
|
|
66
118
|
Example:
|
|
67
119
|
| MockRes.Mock Keyword | My Keyword | return_value=test_data |
|
|
68
120
|
"""
|
|
@@ -75,6 +127,7 @@ class MockResource:
|
|
|
75
127
|
self._original_items[keyword_name] = keyword_runner.keyword.body._items # pylint: disable=protected-access
|
|
76
128
|
mock = Mock(return_value=return_value, side_effect=side_effect)
|
|
77
129
|
self._mocks[keyword_name] = mock
|
|
130
|
+
return mock
|
|
78
131
|
|
|
79
132
|
@keyword
|
|
80
133
|
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.5.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.5.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
|
|
|
@@ -341,6 +342,67 @@ Verify a keyword was called, optionally checking call count.
|
|
|
341
342
|
MockDB.Verify Keyword Called execute_sql times=1
|
|
342
343
|
```
|
|
343
344
|
|
|
345
|
+
### Verify Keyword Called With
|
|
346
|
+
|
|
347
|
+
Verify a keyword was called with the given arguments. Passes when **at least one**
|
|
348
|
+
recorded call matches, so the order of calls does not matter.
|
|
349
|
+
|
|
350
|
+
**Arguments:**
|
|
351
|
+
- `keyword_name` - Name of the keyword to verify
|
|
352
|
+
- `*args` - Expected positional arguments
|
|
353
|
+
- `**kwargs` - Expected named arguments
|
|
354
|
+
|
|
355
|
+
**Example:**
|
|
356
|
+
```robot
|
|
357
|
+
MockDB.Verify Keyword Called With execute_sql SELECT 1 timeout=${30}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Arguments are compared with Python equality, so types matter. Values are
|
|
361
|
+
recorded exactly as Robot Framework passed them to the keyword: test data is
|
|
362
|
+
string data, but Robot converts arguments of keywords that declare argument
|
|
363
|
+
types, and which built-in keywords declare types differs between Robot
|
|
364
|
+
Framework versions. When a recorded value is therefore not a string, give the
|
|
365
|
+
expectation as a typed Robot variable (`timeout=${30}` rather than
|
|
366
|
+
`timeout=30`), or read the call back with `Get Keyword Call Kwargs` and assert
|
|
367
|
+
on it with a type-insensitive comparison.
|
|
368
|
+
|
|
369
|
+
### Get Keyword Call Args
|
|
370
|
+
|
|
371
|
+
Return the positional arguments of one call to a mocked keyword.
|
|
372
|
+
|
|
373
|
+
**Arguments:**
|
|
374
|
+
- `keyword_name` - Name of the mocked keyword
|
|
375
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
376
|
+
|
|
377
|
+
**Example:**
|
|
378
|
+
```robot
|
|
379
|
+
${args}= MockDB.Get Keyword Call Args execute_sql index=0
|
|
380
|
+
Should Be Equal ${args}[0] SELECT 1
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Get Keyword Call Kwargs
|
|
384
|
+
|
|
385
|
+
Return the named arguments of one call to a mocked keyword.
|
|
386
|
+
|
|
387
|
+
**Arguments:**
|
|
388
|
+
- `keyword_name` - Name of the mocked keyword
|
|
389
|
+
- `index` - Zero-based call index, negative counts from the end (default `0`)
|
|
390
|
+
|
|
391
|
+
**Example:**
|
|
392
|
+
```robot
|
|
393
|
+
${kwargs}= MockDB.Get Keyword Call Kwargs execute_sql
|
|
394
|
+
Should Be Equal ${kwargs}[timeout] ${30}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Get Keyword Call Count
|
|
398
|
+
|
|
399
|
+
Return how many times a mocked keyword was called.
|
|
400
|
+
|
|
401
|
+
**Example:**
|
|
402
|
+
```robot
|
|
403
|
+
${count}= MockDB.Get Keyword Call Count execute_sql
|
|
404
|
+
```
|
|
405
|
+
|
|
344
406
|
## How It Works
|
|
345
407
|
|
|
346
408
|
### MockLibrary
|
|
@@ -359,9 +421,12 @@ MockLibrary dynamically replaces keyword implementations:
|
|
|
359
421
|
MockResource patches Robot Framework's keyword execution:
|
|
360
422
|
1. Patches the Namespace.get_runner method
|
|
361
423
|
2. Intercepts keyword execution for the specified resource file
|
|
362
|
-
3.
|
|
363
|
-
|
|
364
|
-
|
|
424
|
+
3. Resolves the call's arguments - variables are replaced, and a `name=value`
|
|
425
|
+
argument becomes a named argument when the keyword declares that name - so
|
|
426
|
+
mocks record what the keyword was really called with
|
|
427
|
+
4. Replaces keyword body with Return statement containing mocked value
|
|
428
|
+
5. Tracks call counts and call arguments for verification
|
|
429
|
+
6. Restores original keyword body on reset
|
|
365
430
|
|
|
366
431
|
### MockCoverage
|
|
367
432
|
|
{robotframework_mock-0.4.0 → robotframework_mock-0.5.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
|