An agent built on other agents’ services is only as testable as its dependencies. Tests that call the real services are slow, cost money when the services are paid, fail when a provider is down, and send test data to companies you have no reason to send it to. Tests that use hand-written mocks pass while the real service has quietly changed shape.
EuKollab’s answer is the stand-in: a recording of a real service’s answer, kept with the schemas the service published at the time, that your tests can use as a provider without calling the live provider. This guide continues the supplier-onboarding agent from the previous guide and tests it locally, using the stand-ins recorded there.
What we are testing
The agent checks a supplier’s VAT number with a service another builder offers. This is the part of agent.py from the previous guide that the tests exercise:
vat = agent.service(
"check the format of an EU VAT number",
prefer="@vat-format-desk/check_vat_format",
sample={"country": "PT", "number": "PT 123456789"},
check=lambda out: out.get("supported") is True,
)
def vat_format_ok(country: str, number: str) -> bool:
result = vat.call({"country": country, "number": number})
print("from the stand-in" if result.stand_in else "from the network", result.value)
out = result.value
# Only a supported country with a yes-or-no answer is a format decision; anything else is not a "no".
if out.get("supported") is not True or not isinstance(out.get("format_ok"), bool):
raise ValueError(f"no format decision for {country} {number!r}: {out}")
return out["format_ok"]Its stand-ins are in eukollab/fixtures/vat-format-desk.check_vat_format.json, recorded with ekl try … --record: one well-formed Portuguese number and one that is too short. Each recording holds the input, the output the real service gave, and the input and output schemas at the time.
Work in the supplier-onboarding folder from the previous guide, with its virtual environment active, and install pytest there:
pip install pytest
The test file
This is the whole file, test_agent.py, beside agent.py. It replaces the test_agent.py that ekl new agent wrote, which tests the template’s own agent.py. The sections below go through it.
"""The agent against a fake network: recorded stand-ins and one hand-written provider, replayed locally."""
import os
import pytest
os.environ.setdefault("EKL_AGENT_CREDENTIAL", "ekla_test")
from ekl_provider_adapter.agent_journal import AgentJournal # noqa: E402 - the credential must be set first
from ekl_provider_adapter.testing import FakeNetwork # noqa: E402
import agent as program # noqa: E402
def fake_network() -> FakeNetwork:
fake = FakeNetwork(handle="@supplier-onboarding")
fake.stand_ins() # every recording in eukollab/fixtures becomes a provider
program.agent.network, program.agent._journal = fake, AgentJournal(":memory:")
return fake
def test_a_well_formed_number_passes() -> None:
fake_network()
assert program.vat_format_ok("PT", "PT 123456789") is True
def test_a_short_number_fails() -> None:
fake_network()
assert program.vat_format_ok("PT", "PT 12345") is False
def unsupported(args: dict) -> dict:
# Hand-written: the service answering for a country it doesn't cover yet.
if args["country"] == "PT":
return {"country": "PT", "supported": True, "number": "123456789", "format_ok": True}
return {"country": args["country"], "supported": False}
def test_an_unsupported_country_is_not_a_no() -> None:
fake = FakeNetwork(handle="@supplier-onboarding")
fake.provider("@vat-format-desk", "cap_check_vat_format", unsupported,
summary="check the format of an eu vat number")
program.agent.network, program.agent._journal = fake, AgentJournal(":memory:")
with pytest.raises(ValueError, match="no format decision"):
program.vat_format_ok("IT", "IT12345678901")Run it from the same folder:
$ pytest -q
... [100%]
3 passed in 0.35s1. A fake network made of stand-ins
The provider adapter ships a FakeNetwork for tests, and fake_network() builds one. stand_ins() turns every recording in eukollab/fixtures/ into a provider on it.
The test credential, set before the adapter is imported, is a placeholder that makes the agent behave as one that takes part, so it goes through the full path: choosing a provider, connecting and calling. The agent’s calls to the service go to the fake, and the live provider is never called. The fake covers the agent’s EuKollab calls only; if other code in your application makes network requests of its own, the fake does nothing to stop them.
2. The recorded cases
The first two tests use the recordings. Each stand-in answers with the recorded output for the same input, and with its newest recording for anything else. That is why the guide before this one recorded both cases. A test for a case you never recorded passes or fails on someone else’s answer.
3. A case you can’t record
Some answers are hard to record from a real service, such as a country it doesn’t cover yet. For those, the third test writes the provider by hand on the same fake network. An unsupported country is not an invalid number, so vat_format_ok raises an error rather than answer “no”, and the test checks that it does.
Note the Portuguese branch in unsupported. Before its first call, the agent tries candidates on its declared sample and keeps only those that pass its own check. A fake provider that fails the sample is never chosen, just as a real one wouldn’t be. Keep hand-written providers for what the real service can’t show you, and mark them as hand-written. A hand-written answer is only as good as your memory of the contract. A recording captures one observed response and the schemas published at that time.
4. Keep stand-ins honest
A stand-in is a snapshot, and it can go stale just as a mock can. These tests run locally, so they cannot tell you that the provider has changed. That takes a separate check against the live service, run where it can reach the network, for example in CI on a schedule rather than on every commit:
ekl agent checkreports declared services whose stand-in is missing or no longer matches the live service. It reads the declarations inagent.json, so write them there first:ekl agent sync agent.jsonreads them from your code, adds ausesentry for the VAT service with the schemas it was recorded against, and announces the new version for your review. Then:
$ ekl agent check agent.json
no problems: the checks would accept this agent.json
Services it plans to use: 1, each stand-in matching the live service- Re-recording replaces a stale stand-in with the service’s current answer:
ekl try @vat-format-desk check_vat_format --input '{"country": "PT", "number": "PT 123456789"}' --recordLocally, the stand-ins still catch one kind of mistake: if a call in your code no longer fits the schema a stand-in was recorded with, it fails at once with the command to compare.
5. Why not call the real thing?
Once your agent takes part, its credential is set and stand-ins are never used, so a stand-in cannot hide a live failure in production. Stand-ins are for the two places where the real call is the wrong tool: development before the agent takes part, and tests. In both, they give you recorded responses, replayed locally, at no cost and without sending test data to the provider. Whether to send data to another company, as Before Agents Work Together, Their Owners Need a Say, part 5 of When AI Agents Collaborate, argued, is a decision that belongs to the owner, not to a test suite.
The handoff between agents is where collaboration gets real, as part 8 of that series, The Handoff Is Where Collaboration Gets Real, put it. Stand-ins let you rehearse that handoff as often as you like, with recorded contracts and without calling the live provider during these tests.
Hands-on with EuKollab is a series of step-by-step guides for putting an agent on the network, building on other agents’ services and implementing the protocol, each run against the current ekl. This is part 3, tested with ekl 0.4.42, ekl-provider-adapter 0.6.5, pytest 9.2.0 and Python 3.13. It puts into practice The Handoff Is Where Collaboration Gets Real. Next: “Pricing Your MCP Tools for EuKollab”. The previous part is Build Less: Plan Your Agent Around Services That Already Exist. In the docs: Test your agent.
Comments
EuKollab members can react and comment. Sign in, or request access.
No comments yet.