agent-tdd skill

v1.0.0 · Agent Skill · pascalai · registry.pascalai.org

Test-driven development for PascalAI agents — write tests first, verify BB state, run with RunTests.exe

tddtestingagentspascalaiquality

Install

ppm install agent-tdd
ppm skill install-claude

The second command activates the skill in Claude Code (copies it into .claude/skills/).

Skill content (SKILL.md)

You are a TDD expert for PascalAI. Write tests first, make them fail, then implement the minimum code to pass.

Test File Structure

PascalAI tests live in tests/examples/. The runner (RunTests.exe or run_demos.ps1) scans this directory.

A test passes when it prints a bare PASS line (no surrounding text). It fails on any other output, exception, or non-zero exit.

program test_my_feature;
begin
  LLM_Connect('claude-opus-4-6', GetEnv('ANTHROPIC_API_KEY'));

  var Result := MyFunction(42);

  if Result = 84 then
    WriteLn('PASS')
  else
  begin
    WriteLn('FAIL: expected 84, got ' + IntToStr(Result));
    Halt(1);
  end;
end.

TDD Workflow

  1. Write the failing test — describe expected behavior
  2. Run pai run test_file.pai — confirm it fails
  3. Implement — write minimum code to make it pass
  4. Run again — confirm PASS
  5. Refactor — clean up, run again

Testing Agents (BB State Approach)

Since agents run asynchronously, verify via Blackboard state:

program test_summarizer_agent;
begin
  LLM_Connect('claude-opus-4-6', GetEnv('ANTHROPIC_API_KEY'));

  BB.Set('input', 'PascalAI is an AI-native language.');
  SummarizerAgent_Spawn;

  Sleep(500);  // wait for cyclic agent to process

  var Summary := BB.Get('summary');
  if Length(Summary) > 10 then
    WriteLn('PASS')
  else
    WriteLn('FAIL: summary too short: ' + Summary);
end.

Testing Without API (Stub Pattern)

Pre-set BB values to simulate LLM output and test agent logic in isolation:

BB.Set('llm_response', 'mocked response');
MyAgent_ProcessResult;  // test handler logic, not the LLM call

if BB.Get('status') = 'done' then
  WriteLn('PASS')
else
  WriteLn('FAIL');

Testing Typed Completions

type TResult = record Score: Double; Label: string; end;

var C: Completion<TResult>;
C := LLM_JSON<TResult>('Classify sentiment: "great product"');

if (C.Value.Score > 0.5) and (C.Value.Label = 'positive') then
  WriteLn('PASS')
else
  WriteLn('FAIL: score=' + FloatToStr(C.Value.Score));

Run All Tests

cd tests
./run_demos.ps1
cd tests
./run_demos.sh

Common Assertions

// String check
if S <> '' then WriteLn('PASS') else WriteLn('FAIL: empty string');

// Numeric range
if (N >= 1) and (N <= 100) then WriteLn('PASS') else WriteLn('FAIL: ' + IntToStr(N));

// BB state after agent runs
if BB.Get('status') = 'done' then WriteLn('PASS') else WriteLn('FAIL: status=' + BB.Get('status'));

// JSON contains key
if Pos('"name"', jsonStr) > 0 then WriteLn('PASS') else WriteLn('FAIL: missing name key');

Naming Convention

NNN_feature_name.pai       — demo/integration test (NNN = sequential number)
test_unit_name.pai         — unit-level test

What to test in every PAI feature