1

I'm starting a fresh python project and I want to write unit and integration tests with mocking and stubbing. However, I would like to run these tests during the build-pipeline against actual services by spawning these dependent services in a docker container. What is the best way to architect my project so that I can easily enable and disable mocking so that:

  • tests are run with mocks in local branches
  • tests are run with actual services (with mocks disabled) in CI build pipeline

I'm using python3 and pytest for my purposes.

2 Answers 2

3

You can create have multiple implementations of your client object.

import abc

class MyServiceClient(abc.ABC):
    @abc.abstractmethod
    def get_item(self, id):
        pass

class MyServiceClientStub(MyServiceClient):
    def get_item(self, id):
        return Item(1)

class RealMyServiceClient(MyServiceClient):
    def get_item(self, id):
        # Make HTTP call

If inside your component you program only against MyServiceClient, you can inject either a MyServiceClientStub or a RealMyServiceClient depending on your runtime variables.

1
  • Thanks! This is a great solution. Jun 1, 2020 at 21:41
0

I have actually found another elegant solution that will work as well depending on your implementation. For my implementation, this worked best.

In your conftest.py file where you have your mocking configured, just put it inside an if/else condition.

# Example conftest.py for your API test
import pytest
import requests

# I'm using this function to define global attributes across all tests
from ..conftest import global_test_attributes
# This is where you have defined your MockResponse Class
from .test_helper import MockResponse


# Any arguments may be passed and mock_get() will always return our
# mocked object, which is defined in test_dataset_get_model_data
@pytest.fixture(autouse=True)
def mock_api_response(monkeypatch):

    """requests.get() mocked to return {'mock_key':'mock_response'}."""

    def mock_get(mock_dataset):
        return MockResponse(mock_dataset)

    # apply the monkeypatch for requests.get to mock_get
    # mocking can be disabled if test attribute is set to "build" mode
    if global_test_attributes()["run_env"] != "build":
        monkeypatch.setattr(requests, "get", mock_get)
        monkeypatch.delattr("requests.sessions.Session.request")
    else:
        pass

This is simple and it works

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.