Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New keywords with assertion engine and require min. Python 3.8 and RF 5.0.1 #215

Merged
merged 10 commits into from
Aug 12, 2024
7 changes: 2 additions & 5 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ jobs:
fail-fast: false
matrix:
include:
- os: 'ubuntu-latest'
python-version: '3.7'
rf-version: '3.2.2'
- os: 'ubuntu-latest'
python-version: '3.8'
rf-version: '4.1.3'
rf-version: '5.0.1'
- os: 'ubuntu-latest'
python-version: '3.9'
rf-version: '5.0.1'
Expand All @@ -29,7 +26,7 @@ jobs:
rf-version: '6.1.1'
- os: 'ubuntu-latest'
python-version: '3.12'
rf-version: '7.0a1'
rf-version: '7.0.1'
runs-on: ${{ matrix.os }}

steps:
Expand Down
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[build-system]
requires = [
"setuptools>=61.0",
"robotframework"
"robotframework>=5.0.1",
"robotframework-assertion-engine"
]
build-backend = "setuptools.build_meta"

Expand All @@ -11,10 +12,11 @@ authors = [{name="Franz Allan Valencia See", email="[email protected]"},
]
description = "Database Library for Robot Framework"
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8.1"
dependencies = [
"robotframework",
"robotframework-excellib"
"robotframework>=5.0.1",
"robotframework-excellib",
"robotframework-assertion-engine"
]
classifiers = [
"Programming Language :: Python :: 3",
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
robotframework
robotframework-excellib
robotframework-assertion-engine
psycopg2-binary
pre-commit
build
twine
22 changes: 16 additions & 6 deletions src/DatabaseLibrary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,20 @@ class DatabaseLibrary(ConnectionManager, Query, Assertion):
The Database Library for [https://robotframework.org|Robot Framework] allows you to query a database and verify the results.
It requires an appropriate *Python module to be installed separately* - depending on your database, like e.g. `oracledb` or `pymysql`.

== Requirements ==
== Table of contents ==
%TOC%

= Requirements =
- Python
- Robot Framework
- Python database module you're going to use - e.g. `oracledb`

== Installation ==
= Installation =
| pip install robotframework-databaselibrary
Don't forget to install the required Python database module!

== Usage example ==
=== Basic usage ===
= Usage example =
== Basic usage ==
| *** Settings ***
| Library DatabaseLibrary
| Test Setup Connect To My Oracle DB
Expand Down Expand Up @@ -65,7 +68,7 @@ class DatabaseLibrary(ConnectionManager, Query, Assertion):
| Check If Not Exists In Database ${sql}
|

=== Handling multiple database connections ===
== Handling multiple database connections ==
| *** Settings ***
| Library DatabaseLibrary
| Test Setup Connect To All Databases
Expand All @@ -89,7 +92,14 @@ class DatabaseLibrary(ConnectionManager, Query, Assertion):
| Switch Database mysql
| Execute Sql String drop table XYZ
|
== Database modules compatibility ==

= Inline assertions =
Keywords that accept arguments ``assertion_operator`` <`AssertionOperator`> and ``expected_value``
perform a check according to the specified condition - using the [https://github.com/MarketSquare/AssertionEngine|Assertion Engine].
| Check Row Count SELECT id FROM person == 2
| Check Query Result SELECT first_name FROM person contains Allan

= Database modules compatibility =
The library is basically compatible with any [https://peps.python.org/pep-0249|Python Database API Specification 2.0] module.

However, the actual implementation in existing Python modules is sometimes quite different, which requires custom handling in the library.
Expand Down
116 changes: 115 additions & 1 deletion src/DatabaseLibrary/assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Optional, Tuple
from typing import Any, Optional, Tuple

from assertionengine import AssertionOperator, verify_assertion
from robot.api import logger


Expand All @@ -30,6 +31,9 @@ def check_if_exists_in_database(
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.

Check if any row would be returned by given the input ``selectStatement``. If there are no results, then this will
throw an AssertionError.

Expand Down Expand Up @@ -67,6 +71,9 @@ def check_if_not_exists_in_database(
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.

This is the negation of `check_if_exists_in_database`.

Check if no rows would be returned by given the input ``selectStatement``. If there are any results, then this
Expand Down Expand Up @@ -106,6 +113,9 @@ def row_count_is_0(
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.

Check if any rows are returned from the submitted ``selectStatement``. If there are, then this will throw an
AssertionError.

Expand Down Expand Up @@ -143,6 +153,9 @@ def row_count_is_equal_to_x(
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.

Check if the number of rows returned from ``selectStatement`` is equal to the value submitted. If not, then this
will throw an AssertionError.

Expand Down Expand Up @@ -181,6 +194,9 @@ def row_count_is_greater_than_x(
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.

Check if the number of rows returned from ``selectStatement`` is greater than the value submitted. If not, then
this will throw an AssertionError.

Expand Down Expand Up @@ -219,6 +235,9 @@ def row_count_is_less_than_x(
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.

Check if the number of rows returned from ``selectStatement`` is less than the value submitted. If not, then this
will throw an AssertionError.

Expand Down Expand Up @@ -247,6 +266,101 @@ def row_count_is_less_than_x(
msg or f"Expected less than {numRows} rows, but {num_rows} were returned from '{selectStatement}'"
)

def check_row_count(
self,
selectStatement: str,
assertion_operator: AssertionOperator,
expected_value: int,
assertion_message: Optional[str] = None,
sansTran: bool = False,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
Check the number of rows returned from ``selectStatement`` using ``assertion_operator``
and ``expected_value``. See `Inline assertions` for more details.

Use optional ``assertion_message`` to override the default error message.

Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.

Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.

Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).

Examples:
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *==* | 1 |
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *>=* | 2 | assertion_message=my error message |
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *inequal* | 3 | alias=my_alias |
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *less than* | 4 | sansTran=True |
| @{parameters} | Create List | John |
| Check Row Count | SELECT id FROM person WHERE first_name = %s | *equals* | 5 | parameters=${parameters} |
"""
logger.info(f"Executing : Check Row Count | {selectStatement} | {assertion_operator} | {expected_value}")
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
return verify_assertion(num_rows, assertion_operator, expected_value, "Wrong row count:", assertion_message)

def check_query_result(
self,
selectStatement,
assertion_operator: AssertionOperator,
expected_value: Any,
row=0,
col=0,
assertion_message: Optional[str] = None,
sansTran: bool = False,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
Check value in query result returned from ``selectStatement`` using ``assertion_operator`` and ``expected_value``.
The value position in results can be adjusted using ``row`` and ``col`` parameters (0-based).
See `Inline assertions` for more details.

*The assertion in this keyword is type sensitive!*
The ``expected_value`` is taken as a string, no argument conversion is performed.
Use RF syntax like ``${1}`` for numeric values.

Use optional ``assertion_message`` to override the default error message.

Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.

Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.

Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).

Examples:
| Check Query Result | SELECT first_name FROM person | *contains* | Allan |
| Check Query Result | SELECT first_name, last_name FROM person | *==* | Schneider | row=1 | col=1 |
| Check Query Result | SELECT id FROM person WHERE first_name = 'John' | *==* | 2 | # Fails, if query returns an integer value |
| Check Query Result | SELECT id FROM person WHERE first_name = 'John' | *==* | ${2} | # Works, if query returns an integer value |
| Check Query Result | SELECT first_name FROM person | *equal* | Franz Allan | assertion_message=my error message |
| Check Query Result | SELECT first_name FROM person | *inequal* | John | alias=my_alias |
| Check Query Result | SELECT first_name FROM person | *contains* | Allan | sansTran=True |
| @{parameters} | Create List | John |
| Check Query Result | SELECT first_name FROM person | *contains* | Allan | parameters=${parameters} |
"""
logger.info(
f"Executing : Check Query Results | {selectStatement} | {assertion_operator} | {expected_value} | row = {row} | col = {col} "
)
query_results = self.query(selectStatement, sansTran, alias=alias, parameters=parameters)

row_count = len(query_results)
assert row < row_count, f"Checking row '{row}' is not possible, as query results contain {row_count} rows only!"
col_count = len(query_results[row])
assert (
col < col_count
), f"Checking column '{col}' is not possible, as query results contain {col_count} columns only!"

actual_value = query_results[row][col]
return verify_assertion(
actual_value, assertion_operator, expected_value, "Wrong query result:", assertion_message
)

def table_must_exist(
self, tableName: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None
):
Expand Down
7 changes: 5 additions & 2 deletions src/DatabaseLibrary/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,11 @@ def row_count(
self.__execute_sql(cur, selectStatement, parameters=parameters)
data = cur.fetchall()
if db_connection.module_name in ["sqlite3", "ibm_db", "ibm_db_dbi", "pyodbc"]:
return len(data)
return cur.rowcount
current_row_count = len(data)
else:
current_row_count = cur.rowcount
logger.info(f"Retrieved {current_row_count} rows")
return current_row_count
finally:
if cur and not sansTran:
db_connection.client.rollback()
Expand Down
28 changes: 28 additions & 0 deletions test/tests/common_tests/assertion_error_messages.robot
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,31 @@ Verify Row Count Is Greater Than X Fails With Message
... Row Count Is Greater Than X
... ${Existing Select} 1
... msg=${Error Message}

Check Row Count With Assertion Engine Fails
${expected value}= Set Variable 5
${expected error}= Catenate
... Wrong row count: '1' (int) should be '${expected value}' (int)
Run Keyword And Expect Error
... ${expected error}
... Check Row Count ${Existing Select} equals ${expected value}

Check Row Count With Assertion Engine Fails With Message
Run Keyword And Expect Error ${Error Message}
... Check Row Count ${Existing Select} less than 1
... assertion_message=${Error Message}


Check Query Result With Assertion Engine Fails
${expected value}= Set Variable ${5}
${expected error}= Catenate
... Wrong query result: '1' (int) should be '${expected value}' (int)
Run Keyword And Expect Error
... ${expected error}
... Check Query Result ${Existing Select} equals ${expected value}


Check Query Result With Assertion Engine Fails With Message
Run Keyword And Expect Error ${Error Message}
... Check Query Result ${Existing Select} less than ${1}
... assertion_message=${Error Message}
18 changes: 18 additions & 0 deletions test/tests/common_tests/basic_tests.robot
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ Retrieve Row Count
Log ${output}
Should Be Equal As Strings ${output} 2

Check Row Count With Assertion Engine
Check Row Count SELECT id FROM person == 2

Check Query Result With Assertion Engine
Check Query Result SELECT first_name FROM person contains Allan

Check Query Result With Assertion Engine - Different Row And Col
Check Query Result SELECT first_name, last_name, id FROM person >= ${2} row=1 col=2

Check Query Result With Assertion Engine - Row Out Of Range
Run Keyword And Expect Error Checking row '2' is not possible, as query results contain 2 rows only!
... Check Query Result SELECT first_name FROM person == Blah row=2

Check Query Result With Assertion Engine - Col Out Of Range
Run Keyword And Expect Error Checking column '5' is not possible, as query results contain 2 columns only!
... Check Query Result SELECT id, first_name FROM person == Blah col=5


Retrieve records from person table
${output}= Execute SQL String SELECT * FROM person
Log ${output}
Expand Down
Loading