Skip to course content
Free CrewAI course

CrewAI for Multi-Agent Automation

Unit 04.04: Testing a tool without the agent

Tools are ordinary functions. That makes them the one part of a crew you can test exhaustively, for free, without a model or an API key.

Call it directly, assert on the result

A CrewAI tool exposes .run(), so a test is a dictionary of arguments and an expected string. No agent, no crew, no key.

The code runs three cases against a conversion tool, including two refusals.

from crewai.tools import tool

RATES = {"USD": 1.0, "EUR": 0.92}


@tool("convert")
def convert(amount: float, currency: str) -> str:
    """Convert an amount from the given currency to USD."""
    if currency not in RATES:
        return f"REFUSED: no rate for {currency}"
    if amount < 0:
        return "REFUSED: amount must not be negative"
    return f"{amount * RATES[currency]:.2f} USD"


CASES = [
    ({"amount": 100.0, "currency": "EUR"}, "92.00 USD"),
    ({"amount": 100.0, "currency": "JPY"}, "REFUSED: no rate for JPY"),
    ({"amount": -5.0, "currency": "USD"},  "REFUSED: amount must not be negative"),
]
passed = 0
for args, expected in CASES:
    got = convert.run(**args)
    ok = got == expected
    passed += ok
    print(f"{'PASS' if ok else 'FAIL'} {str(args):42} -> {got}")

print(f"\n{passed}/{len(CASES)} tool cases pass -- no agent, no model, no key")

# Tools are ordinary functions. Test them like ordinary functions, exhaustively
# and for free, before any agent is allowed near them.

All three cases run in milliseconds and cost nothing. Two of them test refusal paths - the unsupported currency and the negative amount - which are exactly the paths a successful crew run never exercises.

That is the argument for testing tools separately. A crew run tests the happy path through whichever tools the agent happened to choose; a direct test covers every branch you wrote, including the ones that exist specifically for inputs a model might produce.

The mistake this prevents

The mistake is testing tools only through the agent. It is slow, it costs tokens, it is non-deterministic, and it systematically fails to reach the refusal branches - because a well-behaved agent does not produce the malformed input those branches exist for.

Takeaway

Test tools directly with .run(), covering every refusal branch. It is free, deterministic and exhaustive, and it is the only part of the crew where all three are true.