Skip to content

Testing

The best way to test your application is to move the whole configuration inside a middleware that you can then reference in your tests.

If your whole app is contained in a middleware called Kernel and you use PHPUnit your test could look like this:

For HTTP

use Innmind\Framework\{
    Application,
    Environment,
};
use Innmind\OperatingSystem\Factory;
use Innmind\Http\{
    ServerRequest,
    Response,
    Method,
    ProtocolVersion,
};
use Innmind\Url\Url;
use PHPUnit\Framework\TestCase;

final class AppTest extends TestCase
{
    public function testIndex()
    {
        $os = Factory::build(); // or use mocks
        $app = Application::http($os, Environment::test([
            'AMQP_URL' => 'amqp://guest:guest@localhost:5672/',
        ]))->map(new Kernel);

        $response = $app->run(ServerRequest::of(
            Url::of('/'),
            Method::get,
            ProtocolVersion::v20,
        ))->unwrap();

        // $response is an instance of Response
        // write your assertions as usual
    }
}

For CLI

use Innmind\Framework\{
    Application,
    Environment,
};
use Innmind\OperatingSystem\Factory;
use Innmind\CLI\Environment\InMemory;
use PHPUnit\Framework\TestCase;

final class AppTest extends TestCase
{
    public function testIndex()
    {
        $variables = [
            ['AMQP_URL', 'amqp://guest:guest@localhost:5672/'],
        ];
        $os = Factory::build(); // or use mocks
        $app = Application::cli($os, Environment::test($variables))->map(
            new Kernel,
        );

        $environment = $app->run(InMemory::of(
            [], // input chunks
            false, // interactive
            ['entrypoint.php'], // arguments
            $variables,
            '/somewhere/', // working directory
        ))->unwrap();

        $this->assertSame(
            [],
            $environment->outputs()
        );
        $this->assertNull($environment->exitCode()->match(
            static fn($exitCode) => $exitCode->toInt(),
            static fn() => null,
        ));
    }
}

Extending behaviour

Since we use a declarative approach and that Application is immutable we can extend the behaviour of our app in our tests.

Say we want to write functional tests but we have a route that deletes data in a database but we can't verify the data is deleted through our routes. We can add a call to mapRoute so we are in the context of our app and we can inject the test case to write our assertions.

use Innmind\Framework\{
    Application,
    Environment,
};
use Innmind\OperatingSystem\Factory;
use Innmind\Router\Component;
use Innmind\Http\{
    ServerRequest,
    Response,
    Method,
    ProtocolVersion,
};
use Innmind\Url\Url;
use Innmind\Immutable\Attempt;
use PHPUnit\Framework\TestCase;

final class AppTest extends TestCase
{
    public function testIndex()
    {
        $os = Factory::build(); // or use mocks
        $app = Application::http($os, Environment::test([
            'AMQP_URL' => 'amqp://guest:guest@localhost:5672/',
        ]))
            ->map(new Kernel)
            ->mapRoute(static fn($route, $container) => $route->pipe(
                Component::of(function($request, $response) use ($container) {
                    $this->assertSame(
                        [],
                        $container(Services::pdo)
                            ->query('SELECT * FROM some_column WHERE condition_that_should_return_nothing')
                            ->fetchAll(),
                    );

                    return Attempt::result($response);
                }),
            ));

        $response = $app->run(ServerRequest::of(
            Url::of('/some-route/some-id'),
            Method::delete,
            ProtocolVersion::v20,
        ))->unwrap();

        // $response is an instance of Response
        // write your assertions as usual
    }
}