Skip to content

Commit b52eb0a

Browse files
authored
New keywords with assertion engine and require min. Python 3.8 and RF 5.0.1 (#215)
* Keyword 'Check Row Count' using assertion engine * Deprecate old "row count" assertion keywords * Link to Assertion Engine in docs * Deprecate duplicating "check exists" assertion keywords * Log returned number of rows for possible debugging purposes * Put Assertion Engine to dependencies * Require min. Python 3.8 and RF 5.0.1 * improve common docs * New keyword 'Check Query Result' * Typo in docs
1 parent b916815 commit b52eb0a

8 files changed

Lines changed: 192 additions & 18 deletions

File tree

.github/workflows/unit_tests.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@ jobs:
1212
fail-fast: false
1313
matrix:
1414
include:
15-
- os: 'ubuntu-latest'
16-
python-version: '3.7'
17-
rf-version: '3.2.2'
1815
- os: 'ubuntu-latest'
1916
python-version: '3.8'
20-
rf-version: '4.1.3'
17+
rf-version: '5.0.1'
2118
- os: 'ubuntu-latest'
2219
python-version: '3.9'
2320
rf-version: '5.0.1'
@@ -29,7 +26,7 @@ jobs:
2926
rf-version: '6.1.1'
3027
- os: 'ubuntu-latest'
3128
python-version: '3.12'
32-
rf-version: '7.0a1'
29+
rf-version: '7.0.1'
3330
runs-on: ${{ matrix.os }}
3431

3532
steps:

pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[build-system]
22
requires = [
33
"setuptools>=61.0",
4-
"robotframework"
4+
"robotframework>=5.0.1",
5+
"robotframework-assertion-engine"
56
]
67
build-backend = "setuptools.build_meta"
78

@@ -11,10 +12,11 @@ authors = [{name="Franz Allan Valencia See", email="franz.see@gmail.com"},
1112
]
1213
description = "Database Library for Robot Framework"
1314
readme = "README.md"
14-
requires-python = ">=3.7"
15+
requires-python = ">=3.8.1"
1516
dependencies = [
16-
"robotframework",
17-
"robotframework-excellib"
17+
"robotframework>=5.0.1",
18+
"robotframework-excellib",
19+
"robotframework-assertion-engine"
1820
]
1921
classifiers = [
2022
"Programming Language :: Python :: 3",

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
robotframework
22
robotframework-excellib
3+
robotframework-assertion-engine
4+
psycopg2-binary
35
pre-commit
46
build
57
twine

src/DatabaseLibrary/__init__.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,20 @@ class DatabaseLibrary(ConnectionManager, Query, Assertion):
2727
The Database Library for [https://robotframework.org|Robot Framework] allows you to query a database and verify the results.
2828
It requires an appropriate *Python module to be installed separately* - depending on your database, like e.g. `oracledb` or `pymysql`.
2929
30-
== Requirements ==
30+
== Table of contents ==
31+
%TOC%
32+
33+
= Requirements =
3134
- Python
3235
- Robot Framework
3336
- Python database module you're going to use - e.g. `oracledb`
3437
35-
== Installation ==
38+
= Installation =
3639
| pip install robotframework-databaselibrary
3740
Don't forget to install the required Python database module!
3841
39-
== Usage example ==
40-
=== Basic usage ===
42+
= Usage example =
43+
== Basic usage ==
4144
| *** Settings ***
4245
| Library DatabaseLibrary
4346
| Test Setup Connect To My Oracle DB
@@ -65,7 +68,7 @@ class DatabaseLibrary(ConnectionManager, Query, Assertion):
6568
| Check If Not Exists In Database ${sql}
6669
|
6770
68-
=== Handling multiple database connections ===
71+
== Handling multiple database connections ==
6972
| *** Settings ***
7073
| Library DatabaseLibrary
7174
| Test Setup Connect To All Databases
@@ -89,7 +92,14 @@ class DatabaseLibrary(ConnectionManager, Query, Assertion):
8992
| Switch Database mysql
9093
| Execute Sql String drop table XYZ
9194
|
92-
== Database modules compatibility ==
95+
96+
= Inline assertions =
97+
Keywords that accept arguments ``assertion_operator`` <`AssertionOperator`> and ``expected_value``
98+
perform a check according to the specified condition - using the [https://github.com/MarketSquare/AssertionEngine|Assertion Engine].
99+
| Check Row Count SELECT id FROM person == 2
100+
| Check Query Result SELECT first_name FROM person contains Allan
101+
102+
= Database modules compatibility =
93103
The library is basically compatible with any [https://peps.python.org/pep-0249|Python Database API Specification 2.0] module.
94104
95105
However, the actual implementation in existing Python modules is sometimes quite different, which requires custom handling in the library.

src/DatabaseLibrary/assertion.py

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
from typing import Optional, Tuple
14+
from typing import Any, Optional, Tuple
1515

16+
from assertionengine import AssertionOperator, verify_assertion
1617
from robot.api import logger
1718

1819

@@ -30,6 +31,9 @@ def check_if_exists_in_database(
3031
parameters: Optional[Tuple] = None,
3132
):
3233
"""
34+
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
35+
The deprecated keyword will be removed in future versions.
36+
3337
Check if any row would be returned by given the input ``selectStatement``. If there are no results, then this will
3438
throw an AssertionError.
3539
@@ -67,6 +71,9 @@ def check_if_not_exists_in_database(
6771
parameters: Optional[Tuple] = None,
6872
):
6973
"""
74+
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
75+
The deprecated keyword will be removed in future versions.
76+
7077
This is the negation of `check_if_exists_in_database`.
7178
7279
Check if no rows would be returned by given the input ``selectStatement``. If there are any results, then this
@@ -106,6 +113,9 @@ def row_count_is_0(
106113
parameters: Optional[Tuple] = None,
107114
):
108115
"""
116+
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
117+
The deprecated keyword will be removed in future versions.
118+
109119
Check if any rows are returned from the submitted ``selectStatement``. If there are, then this will throw an
110120
AssertionError.
111121
@@ -143,6 +153,9 @@ def row_count_is_equal_to_x(
143153
parameters: Optional[Tuple] = None,
144154
):
145155
"""
156+
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
157+
The deprecated keyword will be removed in future versions.
158+
146159
Check if the number of rows returned from ``selectStatement`` is equal to the value submitted. If not, then this
147160
will throw an AssertionError.
148161
@@ -181,6 +194,9 @@ def row_count_is_greater_than_x(
181194
parameters: Optional[Tuple] = None,
182195
):
183196
"""
197+
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
198+
The deprecated keyword will be removed in future versions.
199+
184200
Check if the number of rows returned from ``selectStatement`` is greater than the value submitted. If not, then
185201
this will throw an AssertionError.
186202
@@ -219,6 +235,9 @@ def row_count_is_less_than_x(
219235
parameters: Optional[Tuple] = None,
220236
):
221237
"""
238+
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
239+
The deprecated keyword will be removed in future versions.
240+
222241
Check if the number of rows returned from ``selectStatement`` is less than the value submitted. If not, then this
223242
will throw an AssertionError.
224243
@@ -247,6 +266,101 @@ def row_count_is_less_than_x(
247266
msg or f"Expected less than {numRows} rows, but {num_rows} were returned from '{selectStatement}'"
248267
)
249268

269+
def check_row_count(
270+
self,
271+
selectStatement: str,
272+
assertion_operator: AssertionOperator,
273+
expected_value: int,
274+
assertion_message: Optional[str] = None,
275+
sansTran: bool = False,
276+
alias: Optional[str] = None,
277+
parameters: Optional[Tuple] = None,
278+
):
279+
"""
280+
Check the number of rows returned from ``selectStatement`` using ``assertion_operator``
281+
and ``expected_value``. See `Inline assertions` for more details.
282+
283+
Use optional ``assertion_message`` to override the default error message.
284+
285+
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.
286+
287+
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
288+
than one connection open.
289+
290+
Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
291+
depending on the database client).
292+
293+
Examples:
294+
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *==* | 1 |
295+
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *>=* | 2 | assertion_message=my error message |
296+
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *inequal* | 3 | alias=my_alias |
297+
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *less than* | 4 | sansTran=True |
298+
| @{parameters} | Create List | John |
299+
| Check Row Count | SELECT id FROM person WHERE first_name = %s | *equals* | 5 | parameters=${parameters} |
300+
"""
301+
logger.info(f"Executing : Check Row Count | {selectStatement} | {assertion_operator} | {expected_value}")
302+
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
303+
return verify_assertion(num_rows, assertion_operator, expected_value, "Wrong row count:", assertion_message)
304+
305+
def check_query_result(
306+
self,
307+
selectStatement,
308+
assertion_operator: AssertionOperator,
309+
expected_value: Any,
310+
row=0,
311+
col=0,
312+
assertion_message: Optional[str] = None,
313+
sansTran: bool = False,
314+
alias: Optional[str] = None,
315+
parameters: Optional[Tuple] = None,
316+
):
317+
"""
318+
Check value in query result returned from ``selectStatement`` using ``assertion_operator`` and ``expected_value``.
319+
The value position in results can be adjusted using ``row`` and ``col`` parameters (0-based).
320+
See `Inline assertions` for more details.
321+
322+
*The assertion in this keyword is type sensitive!*
323+
The ``expected_value`` is taken as a string, no argument conversion is performed.
324+
Use RF syntax like ``${1}`` for numeric values.
325+
326+
Use optional ``assertion_message`` to override the default error message.
327+
328+
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.
329+
330+
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
331+
than one connection open.
332+
333+
Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
334+
depending on the database client).
335+
336+
Examples:
337+
| Check Query Result | SELECT first_name FROM person | *contains* | Allan |
338+
| Check Query Result | SELECT first_name, last_name FROM person | *==* | Schneider | row=1 | col=1 |
339+
| Check Query Result | SELECT id FROM person WHERE first_name = 'John' | *==* | 2 | # Fails, if query returns an integer value |
340+
| Check Query Result | SELECT id FROM person WHERE first_name = 'John' | *==* | ${2} | # Works, if query returns an integer value |
341+
| Check Query Result | SELECT first_name FROM person | *equal* | Franz Allan | assertion_message=my error message |
342+
| Check Query Result | SELECT first_name FROM person | *inequal* | John | alias=my_alias |
343+
| Check Query Result | SELECT first_name FROM person | *contains* | Allan | sansTran=True |
344+
| @{parameters} | Create List | John |
345+
| Check Query Result | SELECT first_name FROM person | *contains* | Allan | parameters=${parameters} |
346+
"""
347+
logger.info(
348+
f"Executing : Check Query Results | {selectStatement} | {assertion_operator} | {expected_value} | row = {row} | col = {col} "
349+
)
350+
query_results = self.query(selectStatement, sansTran, alias=alias, parameters=parameters)
351+
352+
row_count = len(query_results)
353+
assert row < row_count, f"Checking row '{row}' is not possible, as query results contain {row_count} rows only!"
354+
col_count = len(query_results[row])
355+
assert (
356+
col < col_count
357+
), f"Checking column '{col}' is not possible, as query results contain {col_count} columns only!"
358+
359+
actual_value = query_results[row][col]
360+
return verify_assertion(
361+
actual_value, assertion_operator, expected_value, "Wrong query result:", assertion_message
362+
)
363+
250364
def table_must_exist(
251365
self, tableName: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None
252366
):

src/DatabaseLibrary/query.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,11 @@ def row_count(
126126
self.__execute_sql(cur, selectStatement, parameters=parameters)
127127
data = cur.fetchall()
128128
if db_connection.module_name in ["sqlite3", "ibm_db", "ibm_db_dbi", "pyodbc"]:
129-
return len(data)
130-
return cur.rowcount
129+
current_row_count = len(data)
130+
else:
131+
current_row_count = cur.rowcount
132+
logger.info(f"Retrieved {current_row_count} rows")
133+
return current_row_count
131134
finally:
132135
if cur and not sansTran:
133136
db_connection.client.rollback()

test/tests/common_tests/assertion_error_messages.robot

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,31 @@ Verify Row Count Is Greater Than X Fails With Message
103103
... Row Count Is Greater Than X
104104
... ${Existing Select} 1
105105
... msg=${Error Message}
106+
107+
Check Row Count With Assertion Engine Fails
108+
${expected value}= Set Variable 5
109+
${expected error}= Catenate
110+
... Wrong row count: '1' (int) should be '${expected value}' (int)
111+
Run Keyword And Expect Error
112+
... ${expected error}
113+
... Check Row Count ${Existing Select} equals ${expected value}
114+
115+
Check Row Count With Assertion Engine Fails With Message
116+
Run Keyword And Expect Error ${Error Message}
117+
... Check Row Count ${Existing Select} less than 1
118+
... assertion_message=${Error Message}
119+
120+
121+
Check Query Result With Assertion Engine Fails
122+
${expected value}= Set Variable ${5}
123+
${expected error}= Catenate
124+
... Wrong query result: '1' (int) should be '${expected value}' (int)
125+
Run Keyword And Expect Error
126+
... ${expected error}
127+
... Check Query Result ${Existing Select} equals ${expected value}
128+
129+
130+
Check Query Result With Assertion Engine Fails With Message
131+
Run Keyword And Expect Error ${Error Message}
132+
... Check Query Result ${Existing Select} less than ${1}
133+
... assertion_message=${Error Message}

test/tests/common_tests/basic_tests.robot

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ Retrieve Row Count
6363
Log ${output}
6464
Should Be Equal As Strings ${output} 2
6565

66+
Check Row Count With Assertion Engine
67+
Check Row Count SELECT id FROM person == 2
68+
69+
Check Query Result With Assertion Engine
70+
Check Query Result SELECT first_name FROM person contains Allan
71+
72+
Check Query Result With Assertion Engine - Different Row And Col
73+
Check Query Result SELECT first_name, last_name, id FROM person >= ${2} row=1 col=2
74+
75+
Check Query Result With Assertion Engine - Row Out Of Range
76+
Run Keyword And Expect Error Checking row '2' is not possible, as query results contain 2 rows only!
77+
... Check Query Result SELECT first_name FROM person == Blah row=2
78+
79+
Check Query Result With Assertion Engine - Col Out Of Range
80+
Run Keyword And Expect Error Checking column '5' is not possible, as query results contain 2 columns only!
81+
... Check Query Result SELECT id, first_name FROM person == Blah col=5
82+
83+
6684
Retrieve records from person table
6785
${output}= Execute SQL String SELECT * FROM person
6886
Log ${output}

0 commit comments

Comments
 (0)