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

chore(tests): Remove unnecessary/problematic app contexts #28159

Merged
merged 2 commits into from
Apr 24, 2024

Conversation

john-bodley
Copy link
Member

@john-bodley john-bodley commented Apr 20, 2024

SUMMARY

As part of a large change related to [SIP-99B] Proposal for (re)defining a "unit of work", I'm in the process of creating some PRs to clean up some unnecessary/problematic logic to aid with future testing/debugging.

Specifically it seems like at times we're overly pushing an application context where, per the documentation, it states:

Only push a context exactly where and for how long it’s needed for each test. Do not push an application context globally for every test, as that can interfere with how the session is cleaned up.

This is likely the cause of a number of issues I was running into with expired objects in the session not being refreshed.

Finally this PR removes all db.session.expire_all() statements which are not needed. Per my previous statement, these likely were added due to some inconsistencies with how the session was being managed during testing.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

TESTING INSTRUCTIONS

CI.

ADDITIONAL INFORMATION

  • Has associated issue: [SIP-99B] Proposal for (re)defining a "unit of work" #25108
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@john-bodley john-bodley changed the title chore(tests): Remove problematic nested app context chore(tests): Remove unnecessary/problematic app contexts Apr 20, 2024
@john-bodley john-bodley force-pushed the john-bodley-patch-1 branch 4 times, most recently from f2c6f2e to 5afbff9 Compare April 20, 2024 06:32


@pytest.fixture()
def load_unicode_dashboard_with_slice(load_unicode_data):
slice_name = "Unicode Cloud"
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary due to fixture inheritance.

@john-bodley john-bodley force-pushed the john-bodley-patch-1 branch 4 times, most recently from 747acdc to 5403487 Compare April 20, 2024 16:53
dash = _create_unicode_dashboard(slice_name, position)
yield
_cleanup(dash, slice_name)
dash = _create_unicode_dashboard(slice_name, position)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary due to fixture inheritance.

@@ -81,36 +81,6 @@
SCHEMA_ACCESS_ROLE = "schema_access_role"


class TestRequestAccess(SupersetTestCase):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class had no tests associated with it and thus is obsolete.

@john-bodley john-bodley force-pushed the john-bodley-patch-1 branch 2 times, most recently from 83d7d10 to f7ec362 Compare April 20, 2024 17:09
@@ -58,39 +58,36 @@
class TestDashboard(SupersetTestCase):
@pytest.fixture
def load_dashboard(self):
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app context is not needed within the confines of a derived Flask-Testing class.

@@ -37,39 +37,36 @@
class TestDashboardDatasetSecurity(DashboardTestCase):
@pytest.fixture
def load_dashboard(self):
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app context is not needed within the confines of a derived Flask-Testing class.

data_loader.load_table(birth_names_table)
yield
data_loader.remove_table(birth_names_table.table_name)
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to inheritance of fixtures only the outer fixture requires instantiating the app context.

@@ -96,16 +97,14 @@ def get_upload_db():
return db.session.query(Database).filter_by(database_name=CSV_UPLOAD_DATABASE).one()


@pytest.fixture(scope="function")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function scope is the default scope.

get_user,
) -> None:
from superset.commands.report.alert import AlertCommand
from superset.reports.models import ReportSchedule

with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pushed app context is already being leveraged via the app_context fixture specified on line 65.

@@ -35,150 +35,144 @@ def owners(get_user) -> list[User]:
return [get_user("admin")]


@pytest.mark.usefixtures("owners")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to both specify the use of the fixture here in addition to passing it in to the function.

@@ -685,20 +685,19 @@ def test_parse_js_uri_path_items_item_optional(self):
self.assertIsNotNone(parse_js_uri_path_item("item"))

def test_get_stacktrace(self):
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app context is not needed within the confines of a derived Flask-Testing class.

@@ -82,5 +82,4 @@ def test_form_data_to_adhoc_incorrect_clause_type():
form_data = {"where": "1 = 1", "having": "count(*) > 1"}

with pytest.raises(ValueError):
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for an app context to remap the form-data.

@@ -84,10 +84,9 @@
def test_check_sqlalchemy_uri(
sqlalchemy_uri: str, error: bool, error_message: Optional[str]
):
with app.app_context():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary.

@john-bodley john-bodley force-pushed the john-bodley-patch-1 branch 3 times, most recently from 738334f to fba2cf2 Compare April 20, 2024 18:33
@john-bodley john-bodley force-pushed the john-bodley-patch-1 branch 2 times, most recently from 6a26082 to 816fe2a Compare April 20, 2024 20:50
@@ -14,18 +14,16 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# isort:skip_file
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea why this was here.

@@ -70,36 +68,35 @@ def _insert_annotation(


@pytest.fixture()
def create_annotation_layers():
def create_annotation_layers(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
if backend() == "hive":
pytest.skip("Skipping tests for Hive backend")
def skip_by_backend(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

# relying on `tests.integration_tests.test_app.app` leveraging an `app` fixture which is purposely
# scoped to the function level to ensure tests remain idempotent.
# relying on `tests.integration_tests.test_app.app` leveraging an `app` fixture
# which is purposely scoped to the function level to ensure tests remain idempotent.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing app_context fixture cannot be used due to different scopes.

with app.app_context():
yield from _setup_csv_upload()
@pytest.fixture()
def setup_csv_upload_with_context(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
yield from _setup_csv_upload(["public"])
@pytest.fixture()
def setup_csv_upload_with_context_schema(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
yield from _setup_excel_upload()
@pytest.fixture()
def setup_excel_upload_with_context(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
yield from _setup_excel_upload(["public"])
@pytest.fixture()
def setup_excel_upload_with_context_schema(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
cache_manager.data_cache.clear()
yield
def clear_data_cache(self, app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture. That said I'm not entirely sure why it's needed given it's in a class derived from Flask-Testing's TestCase class.

@john-bodley john-bodley force-pushed the john-bodley-patch-1 branch 2 times, most recently from 3896f55 to 8449636 Compare April 20, 2024 21:55
@@ -41,9 +41,7 @@ def create_old_role(pvm_map: PvmMigrationMapType, external_pvms):
pvms = []
for old_pvm, new_pvms in pvm_map.items():
pvms.append(
security_manager.add_permission_view_menu(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Black formatting.

sqllab_role = (
db.session.query(Role).filter(Role.name == "sql_lab").one_or_none()
)
def create_gamma_sqllab_no_data(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
app.config["PUBLIC_ROLE_LIKE"] = "Gamma"
security_manager.sync_role_definitions()
def public_role_like_gamma(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

with app.app_context():
app.config["PUBLIC_ROLE_LIKE"] = "TestRole"
security_manager.sync_role_definitions()
def public_role_like_test_role(app_context: AppContext):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing fixture.

@@ -35,150 +35,144 @@ def owners(get_user) -> list[User]:
return [get_user("admin")]


@pytest.mark.usefixtures("owners")
@pytest.mark.usefixtures("app_context")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use existing fixture.

@john-bodley john-bodley marked this pull request as ready for review April 20, 2024 22:40
Copy link
Member

@betodealmeida betodealmeida left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Left a comment about having a consistent way to pass the app context.

@@ -22,12 +22,12 @@


@pytest.fixture
@pytest.mark.usefixtures("app_context")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not pass app_context as an argument, like in other places, for consistency?

@@ -34,28 +36,28 @@
downgrade = migration_module.do_downgrade


@pytest.mark.usefixtures("app_context")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, should we standardize on either using usefixtures or passing app_context to the test function/fixture?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@betodealmeida I tried to standardize this. It seems that:

  1. If you want to use a fixture in a test you can either pass it as an argument or via @pytest.mark.usefixtures if test functions do not directly need access to a fixture object. Personally I find the later more readable as otherwise when you're reading through the code it feels like a potentially unused argument.
  2. Somewhat cryptic, but if you want to use a fixture in another fixture you have to pass it as an argument*.
  • Per here it states,

Warning: Note this mark has no effect in fixture functions. For example, this will not work as expected:

@pytest.mark.usefixtures("my_other_fixture")
@pytest.fixture
def my_fixture_that_sadly_wont_use_my_other_fixture():
   ...

Currently this will not generate any error or warning, but this is intended to be handled by issue #3664.

Maybe sticking with one pattern throughout is cleaner though—passing it as a function argument. I'm open to suggestions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spoke with @michael-s-molina offline and it seems like he's supportive of the approach I've taken here so I'm going to go ahead and merge this. I think as a follow up it would be good to use the @pytest.mark.usefixtures decorator where possible.

@john-bodley john-bodley merged commit bc65c24 into master Apr 24, 2024
27 checks passed
@rusackas rusackas deleted the john-bodley-patch-1 branch April 24, 2024 20:54
qleroy pushed a commit to qleroy/superset that referenced this pull request Apr 28, 2024
jzhao62 pushed a commit to jzhao62/superset that referenced this pull request May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants