diff --git a/gooddata-sdk/test-requirements.txt b/gooddata-sdk/test-requirements.txt index f1187aa4a..e3987ba3a 100644 --- a/gooddata-sdk/test-requirements.txt +++ b/gooddata-sdk/test-requirements.txt @@ -8,3 +8,4 @@ urllib3==1.26.9 python-dotenv~=1.0.0 attrs>=21.4.0,<=24.2.0 cattrs>=22.1.0,<=24.1.1 +deepdiff~=8.5.0 diff --git a/gooddata-sdk/tests/catalog/test_catalog_workspace.py b/gooddata-sdk/tests/catalog/test_catalog_workspace.py index 09906e637..1182d14a6 100644 --- a/gooddata-sdk/tests/catalog/test_catalog_workspace.py +++ b/gooddata-sdk/tests/catalog/test_catalog_workspace.py @@ -34,6 +34,7 @@ CatalogWebhook, ) from gooddata_sdk.utils import recreate_directory +from tests_support.compare_utils import deep_eq from tests_support.vcrpy_utils import get_vcr from tests.catalog.utils import _refresh_workspaces @@ -165,8 +166,8 @@ def test_get_declarative_workspaces(test_config): expected_o = CatalogDeclarativeWorkspaces.from_dict(data) - assert workspaces_o == expected_o - assert workspaces_o.to_api().to_dict(camel_case=True) == data + assert deep_eq(expected_o, workspaces_o) + assert deep_eq(data, workspaces_o.to_api().to_dict(camel_case=True)) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_declarative_workspaces.yaml")) @@ -610,10 +611,10 @@ def test_put_declarative_workspace(test_config): workspace_o = sdk.catalog_workspace.get_declarative_workspace( test_config["workspace_test"], exclude=["ACTIVITY_INFO"] ) - assert workspace_e != workspace_o + assert not deep_eq(workspace_e, workspace_o) workspace_e.remove_wdf_refs() - assert workspace_e == workspace_o - assert workspace_e.to_dict() == workspace_o.to_dict() + assert deep_eq(workspace_e, workspace_o) + assert deep_eq(workspace_e.to_dict(), workspace_o.to_dict()) finally: _refresh_workspaces(sdk) @@ -634,8 +635,8 @@ def test_store_declarative_workspace(test_config): workspace_id=test_config["workspace"], layout_root_path=path ) - assert workspaces_e == workspaces_o - assert workspaces_e.to_dict(camel_case=True) == workspaces_o.to_dict(camel_case=True) + assert deep_eq(workspaces_e, workspaces_o) + assert deep_eq(workspaces_e.to_dict(camel_case=True), workspaces_o.to_dict(camel_case=True)) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_load_and_put_declarative_workspace.yaml")) @@ -655,8 +656,8 @@ def test_load_and_put_declarative_workspace(test_config): workspace_o = sdk.catalog_workspace.get_declarative_workspace( workspace_id=test_config["workspace"], exclude=["ACTIVITY_INFO"] ) - assert workspace_e == workspace_o - assert workspace_e.to_dict(camel_case=True) == workspace_o.to_dict(camel_case=True) + assert deep_eq(workspace_e, workspace_o) + assert deep_eq(workspace_e.to_dict(camel_case=True), workspace_o.to_dict(camel_case=True)) finally: _refresh_workspaces(sdk) diff --git a/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py b/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py index 8cfa2854f..58eb95cd7 100644 --- a/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py +++ b/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py @@ -26,6 +26,7 @@ ) from gooddata_sdk.compute.model.filter import AbsoluteDateFilter, RelativeDateFilter from gooddata_sdk.utils import recreate_directory +from tests_support.compare_utils import deep_eq from tests_support.vcrpy_utils import get_vcr from tests.catalog.test_catalog_workspace import _refresh_workspaces @@ -91,8 +92,8 @@ def test_store_declarative_ldm(test_config): sdk.catalog_workspace_content.store_declarative_ldm(workspace_id, path) ldm_o = sdk.catalog_workspace_content.load_declarative_ldm(workspace_id, path) - assert ldm_e == ldm_o - assert ldm_e.to_api().to_dict() == ldm_o.to_api().to_dict() + assert deep_eq(ldm_e, ldm_o) + assert deep_eq(ldm_e.to_api().to_dict(), ldm_o.to_api().to_dict()) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_load_and_put_declarative_ldm.yaml")) @@ -109,10 +110,10 @@ def test_load_and_put_declarative_ldm(test_config): sdk.catalog_workspace_content.load_and_put_declarative_ldm(identifier, path, standalone_copy=True) ldm_o = sdk.catalog_workspace_content.get_declarative_ldm(identifier) - assert ldm_e != ldm_o + assert not deep_eq(ldm_e, ldm_o) ldm_e.remove_wdf_refs() - assert ldm_e == ldm_o - assert ldm_e.to_api().to_dict() == ldm_o.to_api().to_dict() + assert deep_eq(ldm_e, ldm_o) + assert deep_eq(ldm_e.to_api().to_dict(), ldm_o.to_api().to_dict()) finally: _refresh_workspaces(sdk) @@ -193,8 +194,8 @@ def test_store_declarative_analytics_model(test_config): sdk.catalog_workspace_content.store_declarative_analytics_model(workspace_id, path) analytics_model_o = sdk.catalog_workspace_content.load_declarative_analytics_model(workspace_id, path) - assert analytics_model_e == analytics_model_o - assert analytics_model_e.to_api().to_dict() == analytics_model_o.to_api().to_dict() + assert deep_eq(analytics_model_e, analytics_model_o) + assert deep_eq(analytics_model_e.to_api().to_dict(), analytics_model_o.to_api().to_dict()) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_load_and_put_declarative_analytics_model.yaml")) @@ -214,8 +215,8 @@ def test_load_and_put_declarative_analytics_model(test_config): analytics_model_o = sdk.catalog_workspace_content.get_declarative_analytics_model( identifier, exclude=["ACTIVITY_INFO"] ) - assert analytics_model_e == analytics_model_o - assert analytics_model_e.to_api().to_dict() == analytics_model_o.to_api().to_dict() + assert deep_eq(analytics_model_e, analytics_model_o) + assert deep_eq(analytics_model_e.to_api().to_dict(), analytics_model_o.to_api().to_dict()) finally: _refresh_workspaces(sdk) @@ -231,8 +232,8 @@ def test_put_declarative_analytics_model(test_config): sdk.catalog_workspace_content.put_declarative_analytics_model(identifier, analytics_model_e) analytics_model_o = sdk.catalog_workspace_content.get_declarative_analytics_model(identifier) - assert analytics_model_e == analytics_model_o - assert analytics_model_e.to_api().to_dict() == analytics_model_o.to_api().to_dict() + assert deep_eq(analytics_model_e, analytics_model_o) + assert deep_eq(analytics_model_e.to_api().to_dict(), analytics_model_o.to_api().to_dict()) finally: _refresh_workspaces(sdk) @@ -248,10 +249,10 @@ def test_put_declarative_ldm(test_config): try: sdk.catalog_workspace_content.put_declarative_ldm(identifier, ldm_e, standalone_copy=True) ldm_o = sdk.catalog_workspace_content.get_declarative_ldm(identifier) - assert ldm_e != ldm_o + assert not deep_eq(ldm_e, ldm_o) ldm_e.remove_wdf_refs() - assert ldm_e == ldm_o - assert ldm_e.to_api().to_dict() == ldm_o.to_api().to_dict() + assert deep_eq(ldm_e, ldm_o) + assert deep_eq(ldm_e.to_api().to_dict(), ldm_o.to_api().to_dict()) finally: _refresh_workspaces(sdk) @@ -269,8 +270,8 @@ def test_get_declarative_analytics_model(test_config): expected_o = CatalogDeclarativeAnalytics.from_dict(data) - assert analytics_model_o == expected_o - assert analytics_model_o.to_api().to_dict(camel_case=True) == data + assert deep_eq(expected_o, analytics_model_o) + assert deep_eq(data, analytics_model_o.to_api().to_dict(camel_case=True)) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_get_declarative_analytics_model_child.yaml")) @@ -286,8 +287,8 @@ def test_get_declarative_analytics_model_child(test_config): expected_o = CatalogDeclarativeAnalytics.from_dict(data) - assert analytics_model_o == expected_o - assert analytics_model_o.to_api().to_dict(camel_case=True) == data + assert deep_eq(expected_o, analytics_model_o) + assert deep_eq(data, analytics_model_o.to_api().to_dict(camel_case=True)) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_get_declarative_ldm.yaml")) @@ -301,8 +302,8 @@ def test_get_declarative_ldm(test_config): expected_o = CatalogDeclarativeModel.from_dict(data) - assert ldm_o == expected_o - assert ldm_o.to_api().to_dict(camel_case=True) == data + assert deep_eq(expected_o, ldm_o) + assert deep_eq(data, ldm_o.to_api().to_dict(camel_case=True)) @gd_vcr.use_cassette(str(_fixtures_dir / "demo_catalog.yaml")) @@ -367,7 +368,7 @@ def test_ldm_store_load(test_config): sdk.catalog_workspace_content.store_ldm_to_disk(test_config["workspace"], path) loaded_ldm = sdk.catalog_workspace_content.load_ldm_from_disk(path) - assert loaded_ldm == ldm + assert deep_eq(ldm, loaded_ldm) @gd_vcr.use_cassette(str(_fixtures_dir / "analytics_store_load.yaml")) @@ -378,7 +379,7 @@ def test_analytics_store_load(test_config): sdk.catalog_workspace_content.store_analytics_model_to_disk(test_config["workspace"], path) loaded_analytics_model = sdk.catalog_workspace_content.load_analytics_model_from_disk(path) - assert loaded_analytics_model == analytics_model + assert deep_eq(analytics_model, loaded_analytics_model) @gd_vcr.use_cassette(str(_fixtures_dir / "label_elements.yaml")) @@ -468,7 +469,7 @@ def test_explicit_workspace_data_filter(test_config): updated_ldm = sdk.catalog_workspace_content.get_declarative_ldm(workspace_id=test_config["workspace"]) - assert model_cpy == updated_ldm + assert deep_eq(model_cpy, updated_ldm) dataset = sdk.catalog_workspace_content.get_full_catalog(workspace_id=test_config["workspace"]).get_dataset( dataset_id @@ -496,6 +497,6 @@ def test_export_definition_analytics_layout(test_config): analytics_e = sdk.catalog_workspace_content.get_declarative_analytics_model( test_config["workspace"], exclude=["ACTIVITY_INFO"] ) - assert analytics_o.analytics.export_definitions == analytics_e.analytics.export_definitions + assert deep_eq(analytics_o.analytics.export_definitions, analytics_e.analytics.export_definitions) finally: _refresh_workspaces(sdk) diff --git a/gooddata-sdk/tests/overview.md b/gooddata-sdk/tests/overview.md index d72b7ef99..e4c29e870 100644 --- a/gooddata-sdk/tests/overview.md +++ b/gooddata-sdk/tests/overview.md @@ -64,3 +64,12 @@ def test_something(): ``` **NOTE**: you do not have to set token value in config file unless you are going to create new recordings. + +You can set the envvar `OVERWRITE=1` when you want to rerecord existing cassettes. + +## Testing on bigger structures + +You are welcome and actually encouraged to use DeepDiff when you are comparing bigger structures. +It can be imported from `tests_support.compare_utils` in the `deep_eq` function. +This simplifies debugging in case of errors. +See other tests how it is used. diff --git a/tests-support/tests_support/compare_utils.py b/tests-support/tests_support/compare_utils.py new file mode 100644 index 000000000..7201c2e6f --- /dev/null +++ b/tests-support/tests_support/compare_utils.py @@ -0,0 +1,12 @@ +# (C) 2023 GoodData Corporation +from __future__ import annotations + +from deepdiff import DeepDiff + + +def deep_eq(expected: any, actual: any) -> bool: + if expected != actual: + print(DeepDiff(expected, actual)) + return False + + return True diff --git a/tests-support/tests_support/vcrpy_utils.py b/tests-support/tests_support/vcrpy_utils.py index c9dc85562..d645cb433 100644 --- a/tests-support/tests_support/vcrpy_utils.py +++ b/tests-support/tests_support/vcrpy_utils.py @@ -2,12 +2,14 @@ from __future__ import annotations import json +import os import typing from json import JSONDecodeError from typing import Any, Optional import vcr import yaml +from vcr.record_mode import RecordMode VCR_MATCH_ON = ("method", "scheme", "host", "port", "path", "query", "body") NON_STATIC_HEADERS = ["DATE", "X-GDC-TRACE-ID"] @@ -22,6 +24,7 @@ def get_vcr() -> vcr.VCR: before_record_request=custom_before_request, before_record_response=custom_before_response, decode_compressed_response=True, + record_mode=RecordMode.ALL if "OVERWRITE" in os.environ else RecordMode.ONCE, ) gd_vcr.register_serializer("custom", CustomSerializerYaml())