Testing

Flows

The Memory datastore and Thread executor allow flows to be configured for easy testing.

Consider the following linear Flow:

from laminar import Flow, Layer
from laminar.configurations import datastores, executors

class TestFlow(Flow): ...

@TestFlow.register
class A(Layer):
    def __call__(self) -> None:
        self.foo = "bar"


@TestFlow.register
class B(Layer):
    def __call__(self, a: A) -> None:
        self.foo = a.foo


@TestFlow.register
class C(Layer):
    def __call__(self, b: B) -> None:
        self.foo = b.foo

Using the results API, it is trivial to execute the flow and make assertions on its outputs.

from laminar.types import unwrap

def test_flow() -> None:
    flow = TestFlow(datastore=datastores.Memory(), executor=executors.Thread())
    execution = flow()

    assert execution.layer(A).foo == "bar"
    assert execution.layer(B).foo == "bar"
    assert execution.layer(C).foo == "bar"

Layers

A Layer is a Python classes, so its functionality can be tested directly by calling the class constructor and then calling it.

# main.py

from typing import Generator

from laminar import Flow, Layer
from laminar.configurations import datastores, executors, hooks

class TestFlow(Flow): ...

@TestFlow.register
class A(Layer):
    def __call__(self) -> None:
        self.foo = "bar"

    @hooks.execution
    def configure_foo(self) -> Generator[None, None, None]:
        yield
        self.foo = self.foo + ".baz"

class B(Layer):
    def __call__(self, a: A) -> None:
        self.foo = a.foo

if flow := TestFlow(datastore=datastores.Memory(), executor=executors.Thread()):
    flow()
# test_main.py

from main import A

def test_A() -> None:
    a = A()
    assert a().foo == "bar"

The Layer constructor also allows the user to set arbitary key/values to help test layers that depend on other layers.

# test_main.py

from main import A, B

def test_B() -> None:
    a = A(foo="bar")
    b = B()
    assert b(a).foo == "bar"

Hooks

Hooks are Python class methods and can be tested directly as well.

# test_main.py

from main import A

def test_configure_foo() -> None:
    a = A(foo="bar")
    next(a.configure_foo())
    assert a.foo == "bar.baz"

Ser(De)

A Protocol is a Python class and can be tested directly.

# test_main.py

from main import Int

def test_int_protocol() -> None:
    assert Int().dumps(1) == b"1"