14
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GraphQLAdvent Calendar 2018

Day 24

Laravel 5.7 + GraphQL(Test編)

Posted at

この記事は GraphQL Advent Calendar 2018 24日目の記事です。
今回はLaravelとGraphQLでテストコードを書いてみました。

シリーズ記事

今回のゴール

記事の登録ミューテーション(CreatePostMutation)のテストをします。
GraphQLのテストなので、LaravelのHTTPテスト(FeatureTest)を使います。
HTTPリクエストを作成し、GraphQLのjson結果をテストします。

環境

  • Laravel: 5.7.15
  • PHP: 7.1.19
  • PHPUnit: 7.4.4

プロダクトコード

Postモデル

app\Post.phpEloquent\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です。

クエリーファイルの配置場所

クエリーファイルの配置場所についてはわかりやすいようあえてテストファイルと同じ場所にしましたが、
クライアント側が参考にできるように、クエリーだけをまとめたディレクトリを用意すると良いかなと思います。

14
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?