この記事は GraphQL Advent Calendar 2018 24日目の記事です。
今回はLaravelとGraphQLでテストコードを書いてみました。
シリーズ記事
- LaravelとGraphQLでAPI開発
- Laravel 5.7 + GraphQL(Install編)
- Laravel 5.7 + GraphQL(ScalarType編)
- Laravel 5.7 + GraphQL(Relation編)
- Laravel 5.7 + GraphQL(Dump Server編)
- Laravel 5.7 + GraphQL(Pagination編)
- Laravel 5.7 + GraphQL(ErrorHandling編)
- Laravel 5.7 + GraphQL(Test編)
今回のゴール
記事の登録ミューテーション(CreatePostMutation)のテストをします。
GraphQLのテストなので、LaravelのHTTPテスト(FeatureTest)を使います。
HTTPリクエストを作成し、GraphQLのjson結果をテストします。
環境
- Laravel: 5.7.15
- PHP: 7.1.19
- PHPUnit: 7.4.4
プロダクトコード
Postモデル
app\Post.php
の Eloquent\Model
を継承したモデルを用意しています。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'title',
'content',
];
/**
* Get the user
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
Post型の定義
app/GraphQL/Type/PostType.php
Post型がどんなフィールドを持っているか定義します。
<?php
declare(strict_types = 1);
namespace App\GraphQL\Type;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Type as BaseType;
use GraphQL;
class PostType extends BaseType
{
protected $attributes = [
'name' => 'PostType',
'description' => 'post type'
];
public function fields() : array
{
return [
'id' => [
'type' => Type::id(),
'description' => 'The id of the post'
],
'user_id' => [
'type' => Type::id(),
'description' => 'The user_id of post',
],
'title' => [
'type' => Type::string(),
'description' => 'The title of post',
],
'content' => [
'type' => Type::string(),
'description' => 'The content of post',
],
'user' => [
'type' => GraphQL::type('UserType'),
'description' => 'The user of post',
],
];
}
}
CreatePostミューテーション
app/GraphQL/Mutation/CreatePostMutation.php
記事の登録を行うミューテーションです。
<?php
declare(strict_types = 1);
namespace App\GraphQL\Mutation;
use Folklore\GraphQL\Support\Mutation;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL;
use Illuminate\Support\Facades\Hash;
use App\User;
use App\Post;
/**
* 記事を登録するミューテーション
*/
class CreatePostMutation extends Mutation
{
/**
* ミューテーション名の定義と概要
*
* @var array
*/
protected $attributes = [
'name' => 'CreatePost',
'description' => 'CreatePost mutation'
];
/**
* ミューテーションが扱う型を定義
*
* @return ObjectType
*/
public function type() : ObjectType
{
return GraphQL::type('PostType');
}
/**
* ミューテーションが取り得る引数を定義
*
* @return array
*/
public function args() : array
{
return [
'user_id' => [
'type' => Type::id(),
'rules' => ['required'],
],
'title' => [
'type' => Type::string(),
'rules' => ['required', 'string'],
],
'content' => [
'type' => Type::string(),
'rules' => ['required', 'string'],
],
];
}
/**
* ミューテーションに対する実処理
*
* @param array $root
* @param array $args
* @return Post
*/
public function resolve($root, $args, $context, ResolveInfo $info) : Post
{
$user = User::findOrfail($args['user_id']);
$post = $user->posts()->create([
'title' => $args['title'],
'content' => $args['content'],
]);
return $post;
}
}
テストコードを書く
さて、いよいよ上記のコードのテストを書きます。
TestCase graphql() 関数を用意する
GraphQLは /graphql
へPOSTするオンリーワンのエンドポイントです。
エンドポイントが決まっているため、TestCaseの抽象クラスにgraphqlメソッドを用意しておくと便利です。
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Foundation\Testing\TestResponse;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
/**
* GraphQL POST request.
*
* @param string $query
* @param array $variables
* @param array $data
* @param array $headers
* @return \Illuminate\Foundation\Testing\TestResponse
*/
protected function graphql(string $query, array $variables = [], array $data = [], array $headers = []): TestResponse
{
$data = [
'query' => $query,
'variables' => json_encode($variables),
] + $data;
return $this->post('/graphql', $data, $headers);
}
}
記事の登録するテスト
tests/Feature/ExampleTest.php
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\User;
class ExampleTest extends TestCase
{
use RefreshDatabase;
/**
* @test
* @return void
*/
public function 記事を登録するテスト()
{
$query = file_get_contents(__DIR__ . '/CreatePostMutation.gql');
$user = factory(User::class)->create();
$variables = [
'user_id' => $user->id,
'title' => 'テストタイトル',
'content' => 'テスト内容',
];
$json = [
'data' => [
'CreatePost' => $variables
],
];
$response = $this->graphql($query, $variables);
$response->assertStatus(200)->assertJson($json);
}
}
tests/Feature/CreatePostMutation.gql
mutation (
$user_id: ID!
$title: String!
$content: String!
) {
CreatePost (
user_id: $user_id
title: $title
content: $content
) {
user_id
title
content
}
}
テスト実行!
$ ./vendor/bin/phpunit
PHPUnit 7.4.0 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 940 ms, Memory: 22.00MB
OK (2 tests, 3 assertions)
OKが返ってきたらOKです。
クエリーファイルの配置場所
クエリーファイルの配置場所についてはわかりやすいようあえてテストファイルと同じ場所にしましたが、
クライアント側が参考にできるように、クエリーだけをまとめたディレクトリを用意すると良いかなと思います。