CircuitPython-mocks documentation

This library contains mock data structures to be used when soft-testing CircuitPython-based projects (with pytest).

Mocking expected behavior

class circuitpython_mocks._mixins.Expecting(**kwargs)[source]

A base class for the mock classes used to assert expected behaviors.

done()[source]

A function that asserts all expectations have been used. This is automatically called from the destructor.

expectations : Deque[I2CRead | I2CWrite | I2CTransfer | I2CScan | SPIRead | SPIWrite | SPITransfer | UARTRead | UARTWrite | UARTFlush | SetState | GetState]

A double-ended queue (deque) used to assert expected behavior.

Example

Examples that use expectations can be found in the

All examples’ source is located in this package’s tests files.

Pytest fixtures

A module that contains pytest fixtures.

These fixtures are made available by defining a pytest_plugins global attribute in the test module (or in conftest.py module).

conftest.py
pytest_plugins = ["circuitpython_mocks.fixtures"]
circuitpython_mocks.fixtures.mock_blinka_imports(monkeypatch: MonkeyPatch)[source]

A pytest fixture that monkey patches the Python runtime’s import paths, such that this package’s mock modules can be imported first (instead of using the adafruit-blinka package).

from circuitpython_mocks.busio.operations import (
    I2CRead,
    I2CWrite,
    I2CTransfer,
    I2CScan,
)

pytest_plugins = ["circuitpython_mocks.fixtures"]


def test_i2c(mock_blinka_imports):
    import board
    from busio import I2C
    from adafruit_bus_device.i2c_device import I2CDevice

    address = 0x42
    # do setup
    with I2C(board.SCL, board.SDA) as i2c_bus:
        i2c_bus.expectations.append(I2CScan([address]))
        assert i2c_bus.try_lock()
        assert address in i2c_bus.scan()
        i2c_bus.unlock()

        # set expectation for probing performed by I2CDevice.__init__()
        i2c_bus.expectations.append(I2CWrite(address, b""))
        # set expectations for I2C bus
        i2c_bus.expectations.extend(
            [
                I2CRead(address, bytearray(1)),
                I2CWrite(address, bytearray(1)),
                I2CTransfer(address, bytearray(1), bytearray(1)),
            ]
        )
        i2c_dev = I2CDevice(i2c_bus, device_address=address)

        # do test
        buf = bytearray(1)
        with i2c_dev as i2c:
            assert not i2c_bus.try_lock()
            i2c.readinto(buf, end=1)
        with i2c_dev as i2c:
            i2c.write(buf, end=1)
        with i2c_dev as i2c:
            i2c.write_then_readinto(buf, buf, out_end=1, in_end=1)

        # assert all expectation were used
        i2c_bus.done()
from circuitpython_mocks.busio.operations import (
    SPIRead,
    SPIWrite,
    SPITransfer,
)
from circuitpython_mocks.digitalio.operations import SetState

pytest_plugins = ["circuitpython_mocks.fixtures"]


def test_spi(mock_blinka_imports):
    from busio import SPI
    from digitalio import DigitalInOut
    import board
    from adafruit_bus_device.spi_device import SPIDevice

    # do setup
    with SPI(board.SCK, board.MOSI, board.MISO) as spi_bus:
        assert spi_bus.frequency == 1000000
        cs = DigitalInOut(board.CE0)

        # set expectations for the CS pin. We'll be doing 3 transactions (toggling pin each time)
        cs.expectations.append(SetState(True))
        cs.expectations.extend([SetState(False), SetState(True)] * 3)
        # set expectations for SPI bus
        spi_bus.expectations.extend(
            [
                SPIRead(bytearray(1)),
                SPIWrite(bytearray(1)),
                SPITransfer(bytearray(1), bytearray(1)),
            ]
        )
        spi_dev = SPIDevice(spi_bus, chip_select=cs)

        # do test
        buf = bytearray(1)
        with spi_dev as spi:
            spi.readinto(buf, end=1)
        with spi_dev as spi:
            spi.write(buf, end=1)
        with spi_dev as spi:
            spi.write_readinto(buf, buf, out_end=1, in_end=1)

        # assert all expectation were used
        spi_bus.done()
from circuitpython_mocks.busio.operations import UARTRead, UARTWrite, UARTFlush

pytest_plugins = ["circuitpython_mocks.fixtures"]


def test_uart(mock_blinka_imports):
    from busio import UART
    import board

    # do setup
    with UART(board.TX, board.RX) as serial:
        # set expectations for UART bus
        serial.expectations.extend(
            [
                UARTRead(bytearray(1)),
                UARTRead(bytearray(1)),
                UARTRead(bytearray(1)),
                UARTWrite(bytearray(1)),
                UARTFlush(),
                UARTRead(None),  # timeout condition
            ]
        )

        # do test
        buf = bytearray(1)
        assert 1 == serial.in_waiting
        _result = serial.read(1)
        serial.readinto(buf)
        _result = serial.readline()
        assert serial.write(buf) == 1
        serial.reset_input_buffer()
        assert serial.read() is None

        # assert all expectation were used
        serial.done()
from circuitpython_mocks.digitalio.operations import SetState, GetState

pytest_plugins = ["circuitpython_mocks.fixtures"]


def test_dio(mock_blinka_imports):
    from digitalio import DigitalInOut, Direction
    import board

    with DigitalInOut(board.D42) as dio:
        assert dio.direction == Direction.INPUT
        assert dio.pull is None

        # set expectations for the pin state changes
        dio.expectations.append(GetState(True))
        assert dio.value
        dio.expectations.extend([SetState(False), SetState(True)])
        dio.switch_to_output()
        dio.value = True

        # assert all expectation were used
        dio.done()

Mocked API