Skip to content

Bug hunt/mockey autospec#965

Open
anisia19 wants to merge 4 commits into
Yelp:masterfrom
anisia19:bug-hunt/mockey-autospec
Open

Bug hunt/mockey autospec#965
anisia19 wants to merge 4 commits into
Yelp:masterfrom
anisia19:bug-hunt/mockey-autospec

Conversation

@anisia19
Copy link
Copy Markdown

@anisia19 anisia19 commented Jun 2, 2026

  • Please check if the PR fulfills these requirements

    • Tests for the changes have been added — the fixes are all in the test layer itself; no new production code was changed

    • Docs have been added / updated — N/A (test-only changes)

    • All CI checks are green — run locally: 1033 passed, 6 xfailed

    • What kind of change does this PR introduce?
      Bug fixes in the test suite, surfaced by adding mockey autospec enforcement.

    • What is the current behavior?
      Four silent test bugs existed:

    1. mock.patch('detect_secrets.audit.io.input') and mock.patch('detect_secrets.core.scan.open') relied on an undocumented implicit create=True that mock.patch applies for builtin names on modules. The intent was invisible and would silently stop working if the names were ever explicitly imported.
    2. test_fails_if_no_line_numbers_found patched detect_secrets.audit.io.clear_screen a second time inside the test body, while the autouse prevent_clear_screen fixture already held an active patch on it.
    3. mock.patch(..., return_value=False) on functions annotated -> bool (is_feature_enabled, verify_aws_secret_access_key) had their return_value silently overridden by mockey's return-type enforcement, causing disable_gibberish_filter to not actually disable anything and test_verify_invalid_secret to always assert VERIFIED_TRUE.
    4. TestAWSKeyDetector and TestAnalyzeLine used setup() instead of setup_method(). pytest 8.x dropped setup() as an alias, so the per-test instance state was never initialised and every test in both classes that touched self.example_key / self.filename failed with AttributeError.
    • What is the new behavior (if this is a feature change)?
      MockAutospecFixture is now active for every test run via tests/init.py + an autouse pytest fixture in conftest.py. All four underlying issues are fixed: create=True is explicit, the double-patch is removed, return_value is set after mock creation, and setup_method() is used throughout.
    • Does this PR introduce a breaking change?
      No. All changes are confined to the test suite and test helpers (testing/mocks.py). No production code was modified.
    • Other information:
      The three mockey-enforced bugs (1–3) were pre-existing silent failures: the tests appeared to pass but were not actually exercising the intended behaviour. Bug 4 was a pytest 8.x compatibility issue unrelated to mockey

anisia19 added 4 commits June 2, 2026 14:49
Problem: mock.patch('module.input') and mock.patch('module.open') relied on
mock.patch's undocumented implicit create=True behaviour for names that appear
in the _builtins set.  Mockey enforces that the attribute already exists on the
target before patching, so it raised AttributeError for both:

  - detect_secrets.audit.io.input  (used in run_logic in audit_test.py)
  - detect_secrets.core.scan.open  (used in test_error_reading_file)

Neither 'input' nor 'open' is imported into those modules; they are resolved
via Python's builtin lookup at call time.  Without create=True the patches
were technically creating a new module attribute behind the scenes and then
deleting it on teardown — the intent was implicit.

Fix: add mockey to the test requirements and initialise it in tests/__init__.py
plus conftest.py (MockAutospecFixture). Add create=True explicitly to both
patches so the intent is visible and mockey skips the autospec check for these
builtin injections.
Problem: test_fails_if_no_line_numbers_found called
mock.patch('detect_secrets.audit.io.clear_screen') inside the test body, but
the autouse fixture prevent_clear_screen in conftest.py already holds an active
patch on that same attribute.  Mockey raises InvalidSpecError when a second
patch tries to autospec an object that is already a Mock — it cannot create an
autospec of a mock.

The duplicate patch was also unnecessary: the autouse fixture guarantees
clear_screen is always mocked, so the only missing piece was a reference to
that existing mock for the assertion.

Fix: expose the mock from prevent_clear_screen by yielding it instead of plain
yield, then accept it as a fixture parameter in the test and assert on it
directly, removing the redundant inner mock.patch call.
…ospec

Problem: mockey enforces return type hints — when a patched function declares
-> bool, _lazy_autospec_method unconditionally sets mock.return_value to a new
_AutospecMagicMock(autospec=bool).  This happened *after* mock.patch had
already applied the return_value kwarg from the patch call, so the caller's
value was silently discarded.

Two places depended on this pattern:

  1. disable_gibberish_filter (testing/mocks.py) patched is_feature_enabled
     (-> bool) with return_value=False.  The override made it return a truthy
     mock instead, so the gibberish filter was never actually disabled.  This
     caused test_supports_stdin to see 'AWSKeyDetector: False' (secret filtered
     out) and test_baseline_filters_out_known_secrets to return exit code 3.

  2. test_verify_invalid_secret patched verify_aws_secret_access_key (-> bool)
     with return_value=False.  The truthy mock caused the detector to always
     return VERIFIED_TRUE regardless of the intended scenario.

Fix: separate mock creation from return_value assignment.  Open the context
manager to get a reference to the mock, then set return_value explicitly inside
the with block, where it overrides whatever autospec installed as the default.
Problem: TestAWSKeyDetector and TestAnalyzeLine both defined a setup() method
to initialise per-test instance state (self.example_key, self.filename, etc.).
In pytest < 8.0 this worked because setup() was recognised as an alias for
setup_method() on non-unittest classes.  pytest 8.x removed that alias —
_register_setup_method_fixture now only looks up 'setup_method' by name, so
setup() was silently never called.  Every test in both classes that read
self.example_key or self.filename failed with AttributeError.

Fix: rename setup() to setup_method() in both classes so pytest 8.x picks it
up correctly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant