Laravel OpenAPI 3 Documentation Verification Using Dredd
You know the story: you write API documentation, things get busy, the documentation rots, and it becomes useless. The Dredd Documentation Testing Framework exists to help solve the problem of rotting documentation. Unfortunately, getting it set up and integrated with Laravel can be a bit tricky. Below I'll outline how to use OpenAPI, Dredd, and Laravel together.
First off, you'll need to write the OpenAPI documentation (previously called Swagger). One thing to note here, make sure to provide examples for all of the items and mark which ones are required so that Dredd knows what to use to fail a test. It's all pretty standard, but it's nice to separate the documentation into multiple files. Here's one way to do so.
---
openapi: 3.0.0
info:
title: Dredd Testing Demo
description: Laravel Dredd Testing Documentation
version: v2
servers:
- url: test.dev
description: test Server
paths:
"/api/posts/{post_id}":
$ref: "./paths/posts.yml"
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
requestBodies:
responses:
I decided to combine multiple YAML files related using $ref
statements into one using an NPM package called json-refs
. However, Dredd only fully supports OpenAPI v2 right now but you wouldn't want to write your docs in OpenAPI v2 since v3 is the current standard, so I decided to use an NPM package called api-spec-converter
convert v3 to v2. Finally, we also need to view the documentation locally to verify it. I used Redoc CLI to handle this.
On the PHP side, the Dredd PHP Hooks library needs to be installed so that the ecosystem can be set up before the tests being run. The problem is how to structure the hooks and integrate them with Laravel since Laravel requires a full application instance to use Models, Factories, and other utilities. One way to do this is to bootstrap Laravel's by first requiring vendor/autoload.php
and then the bootstrap/app.php
file
as depicted here.
I chose to structure the Hooks in individual classes and be able to invoke either a callable anonymous function with the hooks or a method on the class. The entry point is a hookfile that loads composer and then invokes the hook classes.
Inside of the hook classes is where you can use Models or their Factories to set up the environment needed for the test. Also, you can also fail the test inside of the hook by setting $transaction->fail = true
. You can read more about Dredd Hooks, but there's also examples in the repo.
<?php
namespace Tests\Dredd\v1\hooks;
use App\Post;
use Tests\Dredd\AbstractDreddHook;
class PostHook extends AbstractDreddHook
{
public function handle()
{
$this->before('/api/posts/{post_id} > *', 'show');
}
public function show(&$transaction)
{
Post::truncate();
factory(Post::class)->create([
'id' => 1,
'name' => 'foobar',
'text' => "foo bar baz ipsum",
]);
$transaction->fail = false;
}
}
Unfortunately, there's more to APIs than just request and response formats. Since Dredd is mainly supposed to be verifying the structure of the requests and responses, you'll need some way to shut off authentication. I accomplished this by shutting of middleware based on an environment variable. However, this may not work in all circumstances since you may need certain middlewares enabled.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
if (env('DISABLE_MIDDLEWARE', false)) {
$this->app->instance('middleware.disable', true);
}
}
}
To play around with the example project, all you need to do is follow the readme. Try adjusting the OpenAPI documentation and the hooks so that you can see how Dredd works.
All in all, this allows you to automatically verify that your project roughly matches the API documentation which is a serious enhancement compared to trusting humans to ensure correctness.