Interface Segregation with Fake Test Implementations in Python

Defines segregated abstract interfaces (Printer, Scanner) and uses a FakePrinter to record calls for unit testing without real resources.

Easy Python 3.9+ Aug 9, 2026 Testing & modern typing 13 views 0 copies

Python code

46 lines
Python 3.9+
from abc import ABC, abstractmethod


class Printer(ABC):
    @abstractmethod
    def print_document(self, doc: str) -> str:
        pass


class Scanner(ABC):
    @abstractmethod
    def scan_document(self) -> str:
        pass


class MultiFunctionPrinter(Printer, Scanner):
    def print_document(self, doc: str) -> str:
        return f"Printed: {doc}"

    def scan_document(self) -> str:
        return "Scanned content"


class SimplePrinter(Printer):
    def print_document(self, doc: str) -> str:
        return f"Printed: {doc}"


class FakePrinter(Printer):
    """Fake implementation for testing — records calls without real printing."""
    def __init__(self):
        self.calls = []

    def print_document(self, doc: str) -> str:
        self.calls.append(doc)
        return f"Fake printed: {doc}"


if __name__ == "__main__":
    fake = FakePrinter()
    result = fake.print_document("Report.pdf")
    print(result)
    print("Recorded calls:", fake.calls)
    assert isinstance(fake, Printer), "FakePrinter must implement Printer"
    assert fake.calls == ["Report.pdf"], "Fake should record the print call"
    print("Interface segregation + fake test passed")

Output

stdout
Fake printed: Report.pdf
Recorded calls: ['Report.pdf']
Interface segregation + fake test passed

How it works

The ABC and abstractmethod from the abc module enforce the interface contract: any subclass must implement the abstract methods. By splitting MultiFunctionPrinter into separate Printer and Scanner interfaces, clients depend only on the methods they actually use — this is the Interface Segregation Principle. The FakePrinter satisfies Printer while tracking each call in self.calls, making it easy to assert behavior in tests without real hardware. The assert isinstance(fake, Printer) confirms the fake implements the interface, and checking calls verifies the interactions.

Common mistakes

  • Making fake implementations that forget to record calls, so tests can't verify interaction.
  • Creating one monolithic interface that forces classes to implement unused methods.
  • Forgetting to call `super().__init__()` when the base class has state that the fake needs.
  • Using `print` in a fake instead of returning a value expected by the interface.

Variations

  1. Use `unittest.mock.Mock` to auto-create fakes that record calls.
  2. Use `pytest` fixtures to provide fake instances to tests.

Real-world use cases

  • Unit testing a report scheduler that depends on a printer interface, using a fake to capture printable documents.
  • Testing a document scanning workflow with a fake scanner that returns canned scan data, avoiding real hardware.
  • Verifying that a service calls the correct interface methods by inspecting the fake's recorded call list.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Testing & modern typing

Related tutorials and quizzes for this topic.