8000 Pact interactions are not reset if an exception is thrown, causing otherwise passing tests to fail. · Issue #111 · reecetech/pactman · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Pact interactions are not reset if an exception is thrown, causing otherwise passing tests to fail. #111
Open
@chrislawlor

Description

@chrislawlor

If an exception is thrown after entering the Pact context, the _interactions list still contains the expected interaction for that test. This causes all subsequently run tests to fail.

Small test file illustrating this issue:

import os
import pytest
import requests
from pactman import Consumer, Provider


PACTS_DIR = 'pacts'

@pytest.fixture(scope="session")
def pact_session():
    if not os.path.exists(PACTS_DIR):
        os.mkdir(PACTS_DIR)

    return Consumer("TestConsumer").has_pact_with(
        Provider("TestProvider"),
        pact_dir=PACTS_DIR
    )


@pytest.fixture
def pact(pact_session):
    # Comment out this line to "fix" interactions not being cleared
    # pact_session._interactions[:] = []
    return pact_session


def test_1(pact):
    expected = {"test": "test_1"}
    (
        pact.given(None)
        .upon_receiving("Test request 1")
        .with_request("GET", "/")
        .will_respond_with(200, body=expected)
    )

    with pact:
        raise Exception("Simulate fail")
        resp = requests.get(pact.uri + "/")
        assert resp.status_code == 200

def test_2(pact):
    expected = {"test": "test_2"}
    (
        pact.given(None)
        .upon_receiving("Test request 2")
        .with_request("POST", "/")
        .will_respond_with(200, body=expected)
    )

    with pact:
        resp = requests.post(pact.uri + "/")
        assert resp.status_code == 200


def test_3(pact):
    expected = {"test": "test_3"}

    (
        pact.given(None)
        .upon_receiving("Test request 3")
        .with_request("PATCH", "/")
        .will_respond_with(200, body=expected)
    )

    with pact:
        resp = requests.patch(pact.uri + "/")
        assert resp.status_code == 200

To Test

Save the above snipped to a file and run with pytest. Note that all three tests fail.

There won't be a generated pact file since no tests passed.

Now, uncomment the line pact_session._interactions[:] = [], so that the interactions list gets cleared before each test case.

Run the tests, and verify that:

  1. Two tests pass
  2. A pact file is generated containing interactions for the passing tests.

Finally, comment out the raise Exception("Simulate fail") line and run the test again. All three tests will pass, and the generated pact file contains three interactions.

Expected behavior (of unmodified code snipped)

1 test fails, 2 test pass

Actual behavior

3 tests fail

Suggested fix

Either clear the interactions list entirely, or pop the most recent (failed) interaction.

    # mock/pact.py

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        Handler for exiting a Python context.
        Calls the mock service to verify that all interactions occurred as
        expected, and has it write out the contracts to disk.
        """
        self._enter_count -= 1

        if (exc_type, exc_val, exc_tb) != (None, None, None):
            # Remove failed interaction
            self._interactions.pop(0)
            # let the exception go through to the keeper
            return

        # don't invoke teardown until all interactions for this pact are exited
        if self._enter_count:
            return

        self.verify()

        if not self.use_mocking_server and self._auto_mocked:
            self.stop_mocking()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0