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

[MAINTENANCE] Add unit test for MetricsCalculator.get_metric() Method -- as an example template #6179

Conversation

alexsherstinsky
Copy link
Contributor

@alexsherstinsky alexsherstinsky commented Oct 20, 2022

Scope

A tight unit test for the MetricsCalculator.get_metric(MetricConfiguration) method is sought, such that the implementation of this test can be used as an example for developing other tight unit tests.

General Approach

Use the “Mock” approach for as many boundary objects/methods surrounding the core functionality of the method under test.

Particular Approach

  • Since a method on MetricsCalculator is under test and MetricsCalculator is instantiated with ExecutionEngine, use a mock.MagicMock for the ExecutionEngine constructor argument.
  • Since the logic of MetricsCalculator.get_metric(MetricConfiguration) consists of calling MetricsCalculator.get_metrics(Dict[str, MetricConfiguration]) and extracting the key corresponding to metric_name, use mock.patch for the MetricsCalculator.get_metrics return value.
  • Since MetricConfiguration is not under test, use mock.patch for the MetricConfiguration.metric_name and MetricConfiguration.metric_domain_kwargs properties, respectively.
  • Make sure that MetricConfiguration constructor arguments and the mock.patch return values for the corresponding properties and method return values are mutually consistent.

Please annotate your PR title to describe what the PR does, then give a brief bulleted description of your PR below. PR titles should begin with [BUGFIX], [FEATURE], [DOCS], or [MAINTENANCE]. If a new feature introduces breaking changes for the Great Expectations API or configuration files, please also add [BREAKING]. You can read about the tags in our contributor checklist.

Changes proposed in this pull request:

  • JIRA: GREAT-1103/GREAT-1316

After submitting your PR, CI checks will run and @cla-bot will check for your CLA signature.

For a PR with nontrivial changes, we review with both design-centric and code-centric lenses.

In a design review, we aim to ensure that the PR is consistent with our relationship to the open source community, with our software architecture and abstractions, and with our users' needs and expectations. That review often starts well before a PR, for example in github issues or slack, so please link to relevant conversations in notes below to help reviewers understand and approve your PR more quickly (e.g. closes #123).

Previous Design Review notes:

Definition of Done

Please delete options that are not relevant.

  • My code follows the Great Expectations style guide
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added unit tests where applicable and made sure that new and existing tests are passing.
  • I have run any local integration tests and made sure that nothing is broken.

Thank you for submitting!

Alex Sherstinsky added 3 commits October 19, 2022 19:47
…YYYY/alexsherstinsky/validator/metrics_calculator/add_unit_test_for_get_metric-2022_10_19-249
@netlify
Copy link

netlify bot commented Oct 20, 2022

Deploy Preview for niobium-lead-7998 ready!

Name Link
🔨 Latest commit 093750a
🔍 Latest deploy log https://app.netlify.com/sites/niobium-lead-7998/deploys/6351981e9e1a0d000921c4e3
😎 Deploy Preview https://deploy-preview-6179--niobium-lead-7998.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@alexsherstinsky alexsherstinsky requested a review from a team October 20, 2022 03:22
@ghost
Copy link

ghost commented Oct 20, 2022

👇 Click on the image for a new way to code review
  • Make big changes easier — review code in small groups of related files

  • Know where to start — see the whole change at a glance

  • Take a code tour — explore the change with an interactive tour

  • Make comments and review — all fully sync’ed with github

    Try it now!

Review these changes using an interactive CodeSee Map

Legend

CodeSee Map Legend

@alexsherstinsky alexsherstinsky enabled auto-merge (squash) October 20, 2022 03:39
…test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247
Copy link
Member

@Kilo59 Kilo59 left a comment

Choose a reason for hiding this comment

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

It's very unclear what the value or intent of this test is.

I would suggest checking the actual code coverage of this test and make sure it's exercising the sections of the code you intend.

From what I can see, you don't need to use mock.patch at all.

Comment on lines 132 to 134
@mock.patch("great_expectations.execution_engine.execution_engine.ExecutionEngine")
def test_get_metric(execution_engine: mock.MagicMock):
metrics_calculator = MetricsCalculator(execution_engine=execution_engine)
Copy link
Member

@Kilo59 Kilo59 Oct 20, 2022

Choose a reason for hiding this comment

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

We should (almost) never use MagicMock without providing a spec.
https://realpython.com/python-mock-library/#avoiding-common-problems-using-specifications

An Even better alternative would be to define a proper double and just pass in the object to MetricsCalculator(execution_engine=exec_engine_double). Then we have proper isolation and a clear contract, and then we know for certain that a change to ExecutionEngine will never break this test.

class ExecutionEngineStub:
    def some_method_the_test_needs(self) -> str:
           return "foobar"

def test_get_metric_does_something_specific():
    metrics_calculator = MetricsCalculator(execution_engine=ExecutionEngineStub())
    ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Kilo59 We are not using any methods inside ExecutionEngine here -- it is only present as an argument for instantiating MetricsCalculator. Does this change your recommendation? Thanks.

Copy link
Member

@Kilo59 Kilo59 Oct 20, 2022

Choose a reason for hiding this comment

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

No, it just means the double can be very simple (AKA a dummy). As in, it doesn't have to do anything.
It might even be None.

class ExecutionEngineDummy:
     pass

def test_get_metric_does_something_specific():
    metrics_calculator = MetricsCalculator(execution_engine=ExecutionEngineDummy())
    ...
    metrics_calculator = MetricsCalculator(execution_engine=object())
    ...
    metrics_calculator = MetricsCalculator(execution_engine=None)

tests/core/test_metrics_calculator.py Outdated Show resolved Hide resolved
tests/core/test_metrics_calculator.py Outdated Show resolved Hide resolved
tests/core/test_metrics_calculator.py Outdated Show resolved Hide resolved
Comment on lines 138 to 144
"great_expectations.validator.metric_configuration.MetricConfiguration.metric_name",
new_callable=mock.PropertyMock,
return_value=metric_name,
), mock.patch(
"great_expectations.validator.metric_configuration.MetricConfiguration.metric_domain_kwargs",
new_callable=mock.PropertyMock,
return_value=IDDict(metric_domain_kwargs),
Copy link
Member

@Kilo59 Kilo59 Oct 20, 2022

Choose a reason for hiding this comment

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

It seems like you should be using a MetricConfiguationStub here (or the real thing) instead patching in this stuff.
Makes the test much more confusing then it needs to be.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Kilo59 Let us discuss to make sure we agree on the best alternative. Thanks!

Copy link
Member

@Kilo59 Kilo59 Oct 20, 2022

Choose a reason for hiding this comment

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

I'm fine with what you have now, but if you want isolation from the real MetricConfiguation you should be able to just use a namedtuple (or IDDict/DotDict) in place of the real thing.
I still don't see the need for the mock.patch here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Kilo59 The MetricConfiguration object is the one input to get_metric() -- the method under test. So we need it, but its internals must be maximally mocked leaving only the aspects necessary for the test as un-mocked (to prevent test leakage). I updated the docstring to emphasize this. Thanks!

tests/core/test_metrics_calculator.py Show resolved Hide resolved
@Kilo59 Kilo59 requested a review from a team October 20, 2022 14:07
github-actions bot and others added 6 commits October 20, 2022 14:50
…test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247
…metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247' of https://github.com/great-expectations/great_expectations into maintenance/GREAT-1103/GREAT-1316/alexsherstinsky/test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247
…rstinsky/test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247
Alex Sherstinsky added 2 commits October 20, 2022 09:18
Alex Sherstinsky added 2 commits October 20, 2022 09:57
…rstinsky/test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247
@alexsherstinsky alexsherstinsky force-pushed the maintenance/GREAT-1103/GREAT-1316/alexsherstinsky/test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247 branch from e323798 to fedd399 Compare October 20, 2022 16:59
Alex Sherstinsky added 3 commits October 20, 2022 10:06
@alexsherstinsky alexsherstinsky force-pushed the maintenance/GREAT-1103/GREAT-1316/alexsherstinsky/test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247 branch from 3c25be7 to b7367e2 Compare October 20, 2022 18:22
@alexsherstinsky alexsherstinsky merged commit cedb89d into develop Oct 20, 2022
@alexsherstinsky alexsherstinsky deleted the maintenance/GREAT-1103/GREAT-1316/alexsherstinsky/test_metrics_calculator/test_get_metric_unit_test_patched_with_mocks-2022_10_19-247 branch October 20, 2022 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants