> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent eval test suites

## Overview

Make an eval suite a native PHPUnit or Pest assertion. A failed eval fails the surrounding test
with its case ID, description, execution error, and failed expectation details. The framework
packages remain optional: applications only need the adapter for the test runner they use.

JUnit is different: it writes portable XML for CI systems to ingest later. PHPUnit and Pest
reporters actively decide whether the test that is executing now passes or fails.

## Example

```php theme={null}
<?php
require 'examples/boot.php';

use Cognesy\Agents\Builder\AgentBuilder;
use Cognesy\Agents\Capability\Core\UseDriver;
use Cognesy\Agents\Drivers\Testing\FakeAgentDriver;
use Cognesy\Agents\Evals\AgentEval;
use Cognesy\Agents\Evals\AgentEvals;
use Cognesy\Agents\Evals\CanReportAgentEvals;
use Cognesy\Agents\Evals\EvalConfig;
use Cognesy\Agents\Evals\EvalContext;
use Cognesy\Agents\Evals\EvalExitCode;
use Cognesy\Agents\Evals\EvalRunner;
use Cognesy\Agents\Evals\EvalRunResult;
use Cognesy\Agents\Evals\LocalAgentTarget;
use Cognesy\Agents\Evals\PestEvalReporter;
use Cognesy\Agents\Evals\PHPUnitEvalReporter;

// The fake inference driver keeps this example offline and makes the expected reply deterministic.
$target = LocalAgentTarget::fromFactory(static fn() => AgentBuilder::base()
    ->withCapability(new UseDriver(FakeAgentDriver::fromResponses('Verification is required.')))
    ->build());

// This contract remains framework-independent and can be reused by either test runner.
$eval = AgentEval::define(
    description: 'Unverified refund requests require verification.',
    test: static function(EvalContext $t): void {
        $t->send('Refund order A1049.');
        $t->succeeded();
        $t->notCalledTool('refunds_issue');
        $t->messageIncludes('Verification');
    },
)->withId('support/refund-safety');
$suite = new AgentEvals($eval);

// This closure represents the body of a normal PHPUnit test method or Pest `it(...)` test.
// The configured reporter receives the completed run and raises the host framework's assertion
// when any gating eval fails (or when a scored eval fails under strict mode).
$runAsTest = static function(CanReportAgentEvals $reporter) use ($target, $suite): EvalRunResult {
    $config = EvalConfig::default()->withReporter($reporter);
    return (new EvalRunner($target, $config))->run($suite);
};

// Use this reporter in a PHPUnit TestCase. It delegates the final verdict to PHPUnit Assert.
$phpunitResult = $runAsTest(PHPUnitEvalReporter::default());
echo "PHPUnit reporter: PASS\n";

// Use this reporter in a Pest test. It delegates the same verdict to a Pest Expectation.
$pestResult = $runAsTest(PestEvalReporter::default());
echo "Pest reporter: PASS\n";

if ($phpunitResult->exitCode() !== EvalExitCode::Success
    || $pestResult->exitCode() !== EvalExitCode::Success
) {
    throw new RuntimeException('A test-suite reporter did not preserve the successful eval result.');
}

echo "Result: the same eval contract passed in both host test frameworks.\n";
?>
```
