Inline tests in Testo (#[TestInline])

SkillDev tools

Attach #[TestInline] cases directly to production methods so the example/doctest lives next to the implementation. Use when the user asks for "inline tests", "doctests", "examples next to code", or wants quick example-based verification of a pure function without a dedicated test class.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Inline tests in Testo (#[TestInline]) skill

What this skill tells your AI

The instructions your AI receives, as published by php-testo/testo in skills/testo-inline-tests/SKILL.md and read by ahel’s review.

#[TestInline] lets you attach example inputs and an expected return value directly to a method in production code. The Testo runner discovers and executes them like normal tests, but they live next to the implementation — handy for pure functions and library primitives.

This requires the InlineTestPlugin to be enabled for the suite that scans the relevant directory. Fetch https://php-testo.github.io/llms.txt for the attribute signature and llms-full.txt for the plugin wiring details — verify them before generating code, the API surface is small but specific.

Canonical shape

<?php
declare(strict_types=1);

namespace App\Math;

use Testo\Inline\TestInline;

final class Vector
{
    #[TestInline([1, 2],  3)]
    #[TestInline([5, 10], 15)]
    #[TestInline([-1, 1], 0)]
    public static function sum(int $a, int $b): int
    {
        return $a + $b;
    }
}
  • Each #[TestInline] attribute = one case.
  • First argument: array of method arguments (positional).
  • Second argument: expected return value (compared with strict equality).
  • The decorated method must be deterministic and side-effect-free — inline tests aren't for I/O or stateful code.

When inline tests are the right choice

Use #[TestInline] when all of the following hold:

  • The method is pure (same inputs → same output, no side effects).
  • The examples document behaviour that readers of the source will benefit from seeing.
  • Each example fits comfortably on one line.

Otherwise prefer a regular #[Test] class — testo-write-tests skill.

When inline tests are wrong

  • The method has dependencies that need wiring (use DI + a normal test).
  • The expected value isn't a simple literal (no fluent chain, no objects with identity).
  • The test needs setup/teardown — #[TestInline] cannot use #[BeforeTest] hooks.
  • You want to assert exceptions — use Expect::exception(...) inside a regular test.

Configuration check

For inline tests to run, the suite scanning the production directory must include InlineTestPlugin. Excerpt from testo.php:

use Testo\Application\Config\Plugin\SuitePlugins;
use Testo\Inline\InlineTestPlugin;

new SuiteConfig(
    name: 'Sources',
    location: ['src'],
    plugins: SuitePlugins::only(new InlineTestPlugin()),
),

If the user adds #[TestInline] but the test never runs, check the suite plugin list first.

Pitfalls

  • Don't put #[TestInline] on methods that touch globals, the filesystem, time, or randomness.
  • Don't try to label cases with a third argument — case identity is the attribute position. Re-order, don't relabel.
  • A failing #[TestInline] reports the method's FQN + the attribute index as the test identifier. Order matters for diff readability.

Signals

GitHub stars
213
Forks
16
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
testo-inline-tests
Source
github.com/php-testo/testo