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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robotframework-mock
3
- Version: 0.4.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. Replaces keyword body with Return statement containing mocked value
363
- 4. Tracks call counts for verification
364
- 5. Restores original keyword body on reset
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. Replaces keyword body with Return statement containing mocked value
339
- 4. Tracks call counts for verification
340
- 5. Restores original keyword body on reset
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
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = robotframework-mock
3
- version = 0.4.0
3
+ version = 0.6.0
4
4
  author = Lajos Olah
5
5
  author_email = lajos.olah.jr@gmail.com
6
6
  description = A Robot Framework library for mocking keywords in unit tests
@@ -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
- class MockResource:
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
- mock_result = mock(data.args)
46
- keyword_runner.keyword.body._items = [Return(values=[mock_result])] # pylint: disable=protected-access
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
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robotframework-mock
3
- Version: 0.4.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. Replaces keyword body with Return statement containing mocked value
363
- 4. Tracks call counts for verification
364
- 5. Restores original keyword body on reset
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
 
@@ -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
@@ -1,3 +1,4 @@
1
1
  MockCoverage
2
2
  MockLibrary
3
3
  MockResource
4
+ _mock_core