Testing


Overview

Lyger ships with a lightweight testing framework — no PHPUnit required. The base classes TestCase and HttpTestCase provide assertion methods for unit and integration testing.


Writing a Test

Create test files in a tests/ directory in your project:

<?php

use Lyger\Testing\TestCase;
use App\Models\User;

class UserModelTest extends TestCase
{
    public function testCreateUser(): void
    {
        $user = User::create([
            'name'  => 'Alice',
            'email' => 'alice@test.com',
        ]);

        $this->assertNotNull($user->getKey());
        $this->assertEquals('Alice', $user->name);
        $this->assertEquals('alice@test.com', $user->email);
    }

    public function testFindOrFailThrows(): void
    {
        try {
            User::findOrFail(99999);
            $this->fail('Expected exception was not thrown');
        } catch (\Exception $e) {
            $this->assertStringContainsString('not found', strtolower($e->getMessage()));
        }
    }
}

Unit Assertions (TestCase)

Truth Assertions

$this->assertTrue($value);
$this->assertFalse($value);

Equality

$this->assertEquals($expected, $actual);    // == comparison
$this->assertSame($expected, $actual);      // === comparison (type-strict)

Null Checks

$this->assertNull($value);
$this->assertNotNull($value);

Array Assertions

$this->assertContains('expected', $array);
$this->assertArrayHasKey('key', $array);
$this->assertCount(5, $array);

String Assertions

$this->assertStringContainsString('substring', $string);
$this->assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2}$/', $date);

Type Assertions

$this->assertInstanceOf(User::class, $user);

JSON Assertions

$jsonString = '{"name":"Alice"}';
$this->assertJson($jsonString);
$this->assertJsonStringEqualsJsonString('{"a":1}', '{"a":1}');

Fail Explicitly

$this->fail('This test should not reach this point');

HTTP Integration Testing (HttpTestCase)

HttpTestCase extends TestCase and adds fluent HTTP request simulation:

<?php

use Lyger\Testing\HttpTestCase;

class UserApiTest extends HttpTestCase
{
    public function testGetUsers(): void
    {
        $this->get('/api/users')
             ->assertOk()
             ->seeJsonResponse()
             ->assertJsonValid();
    }

    public function testCreateUser(): void
    {
        $this->post('/api/users', [
                 'name'  => 'Bob',
                 'email' => 'bob@test.com',
             ])
             ->assertCreated()
             ->seeJsonResponse();
    }

    public function testGetUserNotFound(): void
    {
        $this->get('/api/users/99999')
             ->assertNotFound();
    }

    public function testUnauthorizedAccess(): void
    {
        $this->get('/api/admin/dashboard')
             ->assertUnauthorized();
    }
}

HTTP Methods

$this->get('/path');
$this->get('/path', ['Authorization' => 'Bearer token123']);  // With headers

$this->post('/path', ['key' => 'value']);
$this->post('/path', $data, ['Content-Type' => 'application/json']);

$this->put('/path/{id}', ['field' => 'value']);
$this->delete('/path/{id}');

All methods return $this for fluent chaining of assertions.


Status Code Assertions

$this->assertStatus(200);        // Custom status code
$this->assertOk();               // 200
$this->assertCreated();          // 201
$this->assertBadRequest();       // 400
$this->assertUnauthorized();     // 401
$this->assertForbidden();        // 403
$this->assertNotFound();         // 404
$this->assertUnprocessable();    // 422
$this->assertServerError();      // 500

Response Assertions

// Assert the response is JSON
$this->seeJsonResponse();

// Assert JSON is valid (parseable)
$this->assertJsonValid();

// Assert JSON has a specific structure (top-level keys)
$this->assertJsonStructure(['data', 'meta', 'links']);

// Assert a specific header
$this->assertHeader('Content-Type', 'application/json');

// Get raw response body
$body = $this->getContent();
$data = json_decode($body, true);

Running Tests

Since Lyger’s test classes are standalone PHP, run them directly:

php tests/UserModelTest.php
php tests/UserApiTest.php

Or create a simple test runner:

// tests/run.php
<?php

require_once __DIR__ . '/../vendor/autoload.php';

$tests = glob(__DIR__ . '/*Test.php');
foreach ($tests as $test) {
    require_once $test;
}

$classes = array_filter(get_declared_classes(), function ($class) {
    return str_ends_with($class, 'Test');
});

$passed = $failed = 0;
foreach ($classes as $class) {
    $instance = new $class();
    $methods  = get_class_methods($class);

    foreach ($methods as $method) {
        if (!str_starts_with($method, 'test')) continue;

        try {
            $instance->$method();
            echo "✓ {$class}::{$method}\n";
            $passed++;
        } catch (\Exception $e) {
            echo "✗ {$class}::{$method}: " . $e->getMessage() . "\n";
            $failed++;
        }
    }
}

echo "\n{$passed} passed, {$failed} failed.\n";
php tests/run.php

Assertion Method Reference

TestCase

Method Description
assertTrue(mixed $val): void Assert value is truthy
assertFalse(mixed $val): void Assert value is falsy
assertEquals($exp, $act): void Assert $exp == $act
assertSame($exp, $act): void Assert $exp === $act
assertNull(mixed $val): void Assert value is null
assertNotNull(mixed $val): void Assert value is not null
assertContains($needle, array $arr): void Assert array contains value
assertArrayHasKey(string $key, array $arr): void Assert key exists
assertCount(int $count, array $arr): void Assert array has N items
assertStringContainsString(string $needle, string $haystack): void Substring check
assertMatchesRegularExpression(string $pattern, string $str): void Regex match
assertInstanceOf(string $class, mixed $obj): void Type check
assertJson(string $val): void Assert valid JSON string
assertJsonStringEqualsJsonString(string $a, string $b): void JSON equality
fail(string $message): void Force test failure

HttpTestCase

Method Description
get(string $uri, array $headers = []): self Simulate GET request
post(string $uri, array $data = [], array $headers = []): self Simulate POST request
put(string $uri, array $data = [], array $headers = []): self Simulate PUT request
delete(string $uri, array $headers = []): self Simulate DELETE request
assertStatus(int $code): self Assert status code
assertOk(): self Assert 200
assertCreated(): self Assert 201
assertBadRequest(): self Assert 400
assertUnauthorized(): self Assert 401
assertForbidden(): self Assert 403
assertNotFound(): self Assert 404
assertUnprocessable(): self Assert 422
assertServerError(): self Assert 500
seeJsonResponse(): self Assert response is JSON
assertJsonValid(): self Assert JSON is parseable
assertJsonStructure(array $keys): self Assert JSON keys exist
assertHeader(string $key, string $value): self Assert header value
getContent(): string Return raw response body

Copyright © 2026 Lyger Framework. Distributed under the MIT License.

This site uses Just the Docs, a documentation theme for Jekyll.