NAKKA-Kの技術ブログ

技術に関する知見や考え方などを投稿します。

Laravel5.7でAPIコントローラーをメソッドとしてテストする方法

LaravelでAPIやコントローラーをテストする方法を調べると、大体出てくるのはサーバーを立ち上げてルーティングに対してリクエストを飛ばしてテストする方法です。 今回はコントローラーのメソッドをそのままテストする方法について記述していきます。

なぜそうしたのか?

簡単に言うとテストが軽くなるからです。 メソッドを一つテストするのにすぎず、サーバーとしての機能を一切使う必要がありません。 もちろんこのテストが通ったからといって、ルーティングなどその他の部分が正しいことを担保することはできません。 しかし一番重要であるコントローラーのメソッドが通ることは担保できます。 個人開発として素早く開発していきたかったので、最小限のコストでクリティカルな部分を担保したかったのでこの方式でテストすることにしました。

やること

基本的なテストクラスの記述は普通のテストとなんら変わりません。 Laravel(composer)の機能で自動ファイル作成してもらって構いません。

ここでやることは以下です。

  • ログインする
  • 任意のリクエストを送る
  • コントローラーのメソッドを実行する
  • 戻ってきたステータスをテストする

テストコード

今回はBookControllerという本を司るコントローラーに、本の作成依頼をするテストをします。

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;

class UserBookControllerTest extends TestCase
{
    use RefreshDatabase;

    protected $user;

    public function setUp()
    {
        parent::setUp();
        $this->user = factory(User::class)->create();
    }

    public function testStoreBook(){
        $this->actingAs($this->user, 'api');

        $request = new Request(['isbn' => '9784063842760']);
        $response = \App::make(BookController::class)->store($request, $this->user);
        $this->assertEquals(201, $response->status());
    }
}

まずユーザーをfactoryで作成します。 factoryの説明はこちらをご覧ください。

このコントローラーへのアクセスはログインが必要なので$this->actingAsを使ってログインします。 引数にユーザーを渡し、第二引数にapiでログインすることを明示します。

次にリクエストを作成します。 Requestのコンストラクタに連想配列を渡してあげるだけです。

次にApp::makeメソッドを使用してテストしたいコントローラーを指定します。 戻り値にコントローラーが帰ってくるのでテストしたいAPIのメソッドを呼び出します。 今回はstore(Request $request, User $user)をテストします。

するとレスポンスが帰ってきますので、statusメソッドを実行すればステータスコードを見ることができます。 もしレスポンスデータもテストしたい場合は$response->getData()を実行ればデータが連想配列で帰ってきます。

モック化

コントローラーをテストするときに、内部で重い処理や外部への問い合わせをしている場合はモック化したいと思います。 Laravelのモック化については記事の範囲外なので、以下の記事を参考にしてください。

nakka-k.hatenablog.com

まとめ

テストが早いと開発の効率もガンガン上がりますよね。 テストは書きたいけど、遅くなるのは嫌だと言う人は、この方法でテストすればある程度テストの実行時間を落とせるかもしれませんね。 良いテスト生活を!

Laravel5.7のServiceコンテナをモック化してする方法

Laravelに限らずですが外部のAPIを通信したり、重い処理を実行しているの部分があるのでテストの時はモックにしたい、といった場面は多々あると思います。 今回はServiceコンテナをモック化する方法をまとめていきます。

Serviceコンテナが使われているコード

    public function store(Request $request, User $user)
    {
        // ScrapeManagerの生成
        $scrapers = resolve('app.bookInfo.scrapeManager');

        // ScrapeManagerの実行
        $newBook = $scrapers->searchByIsbn((string)$isbn);

        // ......

Serviceコンテナのキーを使ってresolveで生成しています。 この中身をモック化できればいい感じにテストができそうです。

(Managerなんて責務の大きい名前を使ってんじゃねぇ、という意見は却下します)

テスト内でモック化する

    public function setUp()
    {
        // モックを作成
        $mock = \Mockery::mock(ScrapeManager::class)
            ->shouldReceive('searchByIsbn')
            ->andReturn($this->book)
            ->getMock();

        // モックを適用
        $this->app->bind('app.bookInfo.scrapeManager', function() use ($mock) {
            return $mock;
        });
    }

    public function tearDown()
    {
        parent::tearDown();
        \Mockery::close();
    }

実はこれだけです。 Mockery::mockでモック化対象のクラスを指定します。 shouldReceiveで対象メソッドを指定します。 andReturnで対象メソッドの戻り値を設定します。 あとは最後にgetMockを実行してモックのインスタンスを獲得することを忘れないようにしてください。

次に作成したモックをresolveで解決できる形に設定する必要があります。 $this->app->bindを使います。 第一引数にresolve時に使うキーを指定し、第二引数にはモックを返す関数を設定します。

テスト終了後にMockery::close()を実行することを忘れないようにしましょう。

あとはただただテストをするだけです。 そうすれば処理の中でモック化したキーのresolveが使われれば、モックオブジェクトが呼び出されて任意の処理になります。

テストについて

Laravelでコントローラーテストをする場合にはこちらの記事をご覧ください。

nakka-k.hatenablog.com

まとめ

処理の重い部分、特に外部に影響を与える部分はちゃんとモック化しましょう!! 良いテスト生活を!

vimのGoの言語補助プラグインでUnknown functionバグが発生した

vimをupgradeさせた時に唐突にvimがエラーを吐き出し始めました。 エラーは吐き出すもののとりあえず動いてはいました。 ですがvim-goのある部分でエラーが発生しているようでした。

Error on startup: Unknown function: go#config#CodeCompletionEnabled

とりあえずvim-goのissueを探していると今回の問題に関係ありそうなissueを発見しました。

github.com

このissueに書いてあることを要約すると「sheerun/vim-polyglotから使用される言語系プラグインを、vim-polyglotより先に読み込んではいけない。」というものでした。 vim-polyglotを最後に読み込むようにコードを書き換えて再度読み込みすれば解決しました。 私の場合はdein.vimを使っていたのでキャッシュを削除してもう一度プラグインのインストールを走らせる必要がありました。

webアプリケーション開発で個人的に重視しているテストの話

Slackでテストの話について盛り上がったので、webアプリケーション開発で個人的なテスト毎の優先順位などについて書きたいと思います。 早速本題に入りたいと思います。

Controller

コントローラーの処理をホワイトボックステストする利点は少なく、ブラックボックステストの方がいいと思います。

特にE2Eテストはとんでもなく変更コストが高いので割と否定派です。 サービスの根幹をなす部分に関してのみ絶対の信頼を担保したい場合書く、というくらいでいいと思います。

E2Eテストについて書いた記事もありますので詳しく見たければそちらもご覧ください。

qiita.com

コントローラー周りのテストとして、アクセスして期待したstatus(200番など)か観測する、という単純なテストならコストがかなり低くメリットもあると思っているので割と書くことが多いです。

Model

やはりwebアプリで一番重要なテストはモデルのテストです。

システムは全ての箇所でモデルを操作することになりますし、システムで一番重要なのはいかにデータを操作するかです。 ですのでモデルをテストするだけで全体のテスト網羅率は跳ね上がり正確性を担保できます。 モデルテストを全然書かないシステムは最悪です。 一番根幹の部分が担保されていないシステムはガタガタになってしまうからですね。

View(UI)

UIテストに関してはほぼ書くことはありません。

ごくごく偶に書かれることはありますが、さほど役立った経験はありません。 Railsなどにおいてはまず書かないものとしてプラクティスが出来上がっています。

Unit

単体テストに関してはwebであろうとなんであろうと同じ程度に重要です。

しかし外からは見えないはずのpublic以外のメソッドをテストするのはほぼ死刑です。 カプセル化を破壊せずにpublicメソッドを通じてブラックボックステストしてください。

まとめ

テストの優先度としては、モデルテスト > アクセスのテスト > もう少し詳細なコントローラーテスト > E2Eテスト > UIテスト、という形に落ち着くかと思います。 単体テストはいろんなところで発生するものなのでここでの順位付けはしませんでした。

テストといえばTDDについての話もありますがそれについてはここでは触れませんが、webアプリケーションの作成でTDDというのは割と上級者向けかなと個人的に思っています。 TDDについては過去におすすめのTDD本について書いているのでそちらをご覧ください。

nakka-k.hatenablog.com

Laravel5.7 [API開発]で権限設定をPolicyに任せて幸せになろう

LaravelでAPIを作っていると、例外周りの処理を良い感じにしてくれないので非常に大変です。 Policyをそのまま使っても403ページが返ってしまいますし、エラーメッセージの変更も困難です。 今回は権限判定をPolicyに移植して、かつ任意のエラーメッセージをjsonで返せるようにした手順をご紹介します。

FormRequestについての記事もあります。 nakka-k.hatenablog.com

Policyとは

Laravelには特定のモデルやリソース、処理に対して権限を判定し認可・非認可する方法があります。 その方法には大きく分けてGateとPolicyの2つがありますが、今回はPolicyについてのみ説明します。

Policyとは、モデルなどのデータに対してユーザーが行った動作を認可・非認可するために使われる仕組みです。 例えばPostモデルを編集する権限はPostを投稿したユーザーのみにしかない、といった権限判定に使われます。

今まではこんな書き方をしていた

ソースコードは一部改変しています

ユーザーそれぞれが持つ本棚に対して設定を変更する処理を書いているコントローラーです。 コントローラー内で編集先のユーザーIDとログインしているユーザーIDを比較して、許可されていない編集だと判定した場合に403のjsonレスポンスを返しています。

class UserBookController extends Controller
{
    public function update(Request $request,  $user, $userBookId)
    {
        // 認可チェック
        if(auth()->guard('api')->id() != $user->id){
            return response()->json(
                [
                    'status' => 403,
                    'errors' => ['自分以外の本棚を編集することはできません。']
                ],
                403
            );
        }

        // success authorized ......
    }
}

コントローラー内でいちいち認可チェックを書いていてはコントローラーが肥大化しますし、コードを間違えて本来許可されていない動作を許してしまう可能性が増えてしまいます。 やはり認可は認可だけ別の場所に移動させてしまいたいですね。

Laravelでモデル操作の認可に関する処理を書く場合はPolicyを使うべし、と公式ドキュメントに書かれていたのでPolicyを作っていきましょう。

Policyを作成する

Policyはモデルと1対1で結びつく物です。 基本的なCRUD操作に対してそれぞれ認可判定を書けるようになっています。

ファイル生成

今回はUserBookというモデルに対してPolicyを生成しますので--modelオプションでモデルを指定しています。 実際に生成されたメソッドを全て使うわけではありませんが、最初に全体を見ていた方が分かりやすいので生成した後に使わないメソッドを消していく手法を取っています。

$ php artisan make:policy UserBookPolicy  --model=UserBook

もちろん--modelオプションは付けずとも作ることができます。

コマンドを実行するとapp/Policies/が作成され、その下にこのようなファイルが作成されます。

思った以上にメソッドが多いですが簡単にまとめると以下のような役割になっています。

  • view: 閲覧
  • create: 作成
  • update: 更新
  • delete: 削除
    • 論理削除を実装している場合、物理削除ではなく論理削除する時
  • restore: 論理削除状態から復元する時
    • 論理削除を実装している場合のみ
  • forceDelete: 物理削除
    • 論理削除を実装している場合のみ

処理記述

今回はupdateを実装していきます。

Policyの各メソッドはBoolean返す必要があります。 認可だとtrue、非認可だとfalseを返すため条件分をそのまま戻り値に書いてあげましょう。 もちろんifで制御しても良いです。

    public function update(User $user, UserBook $userBook)
    {
        return $user->id == $userBook->user_id;
    }

次はPolicyをモデルに紐づけて認可チェックをしてくれるように設定しましょう。

Policyを組み込む

まずapp/Providers/AuthServiceProvider.phpを編集します。 このファイルには$policiesという連想配列を持つ変数があります。 そこにモデルとPolicyの対応づけを追加しましょう。

    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
        'App\UserBook' => 'App\Policies\UserBookPolicy'
    ];

そして追加したPolicyを反映させるために、コントローラーの__constructミドルウェアの設定をします。 今回はコントローラーでミドルェアの設定をしますがどこで設定しても構いません。(参考)

class UserBookController extends Controller
{
    public function __construct(){
      $this->middleware('can:update,userBook')->only('update');
    }

これによりUserBookモデルのDI時にUserBookPolicyの認可チェックを走らせ、場合によって403のレスポンスを返してくれるようになります。

ですがここで問題が発生します。 基本的にLaravelの例外処理はhtmlページを返すようになっています。 これではAPIとして問題がありますので、次はjsonを返すようにしていきましょう。

エラーハンドリングのためにLaravelのコードリーディングをする

はい。まず結論から申しますとPolicyにレスポンスを動的に記述するメソッドは見つかりませんでした。 いろいろ調べてもよく分からなかったので軽くLaravelのコードリーディングをしてみましょう。

Policyのファイルを見てみるとuse HandlesAuthorization;というトレイトをuseしている行があります。 Policyクラスは継承などをしていないようなので、ほぼ全ての動作がこのuse HandlesAuthorization;に集約されていそうです。 このトレイトはIlluminate\Auth\Accessネームスペースのようなので、まずそのファイルを見てみましょう。

gist.github.com

allowdenyという2つのメソッドだけありますね。 なんとなく認可・非認可に使われるメソッドのようですし、失敗した時はdenyにエラーメッセージが渡されているようです。

これだけでは全然わかりませんね。 まずはdenyメソッドの戻り値であるAuthorizationExceptionを見てみましょう。

gist.github.com

なんというか何も書いていませんね。 でもまあException(例外)であることだけは分かります。

ではもう一度HandlesAuthorizationに戻って、次はallowメソッドの戻り値であるIlluminate/Auth/Access/Responseを見ていきましょう。

gist.github.com

戻り値のラッパーでしかなさそうですね。 とりあえずスルーしましょう。

んー、なんだか遡れそうな先がなくなりました。 では同じnamespaceにあるまだ見ていないファイルも見てみたいので同じディレクトリにあるIlluminate/Auth/Access/Gateも読んでみましょう。

(ファイルが大きすぎるのでリンク先でご覧ください。)

とりあえずHandlesAuthorizationで使われていたallowdenyの呼び出しとかがないか調べてみましょう。

なんだかauthorizeというメソッドに使われている痕跡がありますね。 レスポンスの型も同じですね。

    /**
     * Determine if the given ability should be granted for the current user.
     *
     * @param  string  $ability
     * @param  array|mixed  $arguments
     * @return \Illuminate\Auth\Access\Response
     *
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function authorize($ability, $arguments = [])
    {
        $result = $this->raw($ability, $arguments);
        if ($result instanceof Response) {
            return $result;
        }
        return $result ? $this->allow() : $this->deny();
    }

ここからもいろいろ遡れますがあまり有益な情報は得られませんでした。 私のLaravelのコードリーディング力の無さが露見しますね。

一番有益な情報もありました。 AuthorizationExceptionにメッセージを渡してthrowすればなんとなく上手くいきそうな気がします。

ではその方針で進めていきます。

エラーをjsonで返すようにする

まずはjsonを返すようにしましょう。 Policyで非認可されればAuthorizationExceptionが帰ってくるようなので、その例外をキャッチしてjsonを返すように処理を書きます。

レスポンスの最終的なエラーハンドリングを変更するにはapp/Exceptions/Handler.phprenderメソッドに追記していきましょう。

    public function render($request, Exception $exception)
        if ($exception instanceof AuthorizationException) {
            return response()->json([
                'errors' => [$exception->getMessage()]
            ], 403);
        }

        return parent::render($request, $exception);
    }

例外である$exceptionの型を判定してAuthorizationExceptionであればjsonを返すように記述しましょう。 jsonにエラーメッセージを含めるために$exception->getMessage()をレスポンスに入れましょう。 ここで返されるメッセージはHandlesAuthorizationdenyメソッドのデフォルト引数だった'This action is unauthorized.'です。

次はエラーメッセージを動的に変更できるようにしましょう。 Policyのファイルを編集します。私の場合app/Policies/UserBookPolicy.phpですね。

以前は条件文を返すだけだったのでBooleanでしたね。 ここでAuthorizationExceptionを返すようにしてしまいます。

    public function update(User $user, UserBook $userBook)
    {
        if ($user->id == $userBook->user_id) {
            return true;
        }
        throw new AuthorizationException('自分以外の本棚を編集することはできません。');
    }

認可する場合は今まで通りtrueを返してあげますが、falseを返していたタイミングではAuthorizationExceptionthrowします。 ここで投げられたAuthorizationExceptionは先ほど書いたエラーハンドリングメソッドであるrenderメソッドでキャッチされます。 AuthorizationExceptionに渡した引数が$exception->getMessage()で呼び出されるメッセージになります。

実際のレスポンスを見てみましょう。

{
    "errors": [
        "自分以外の本棚を編集することはできません。"
   ]
}

想定通りのレスポンスになっていますね! ということはこれで、jsonを返しつつエラーメッセージも動的に変更することができました!!

最終的にできたコード

https://gist.github.com/NAKKA-K/3d296424fd0c2568ceb6f5d91157c8a5

PolicyとFormRequestを併用

今回の記事では殆ど出てきませんでしたがFormRequestと言う、POSTなどで送られてきたデータをバリデーションしてくれる機能があります。 FormRequestとPolicyを併用した場合、しっかりとPolicyで権限をチェックした後にリクエストのバリデーションをしてくれます。 なので安心してFormRequestとPolicyを併用してください。

FormRequestの導入方法は以下の記事をご覧ください。

nakka-k.hatenablog.com

まとめ

やはり権限周りのチェックはかなり重要な機能です。 確実に権限判定の処理を書くためにPolicyに責任分離し、しっかりと実装しておきましょう。

Policyに分離することで責任分離もできますし、コントローラーの記述量がかなり少なくできます。 どんどん使っていきましょう。

参考

Laravelを使ったAPI開発でController内のバリデーションをFormRequestに抽出して幸せになろう

私の開発しているプロジェクトでは

  • Laravel 5.7
  • React/Redux

を使って開発しています。

バックエンドのLaravelはAPIを実装しています。 LaravelでAPIを実装すると通常のwebで作るより処理が煩雑になる気がします。 その上、いろんな処理をControllerにまとめて書いてしまった経験はそれなりにあるのではないでしょうか?

今回はリクエストのバリデーション処理をControllerに書いている状態から、LaravelのFormRequestを使って責務を抽出した手順をご紹介します。

(軽く調べた感じLaravel5.5以降でないと違う書き方になるようです)

Policyについての記事もあります。

nakka-k.hatenablog.com

FormRequestとは

FormRequestとはリクエストのバリデーションルールを定義するLaravelの仕組みです。 FormRequestをコントローラーメソッドにDIしてあげると、バリデーションが通った時だけコントローラー内の処理が走ります。

これによりコントローラーはリクエストに不正な値が入っている可能性を除外できるため、自分の処理だけに専念できます。

Controllerにバリデーションを書いていた時

APIでバリデーションを書くと割と面倒でした。 例えばこれはログインする時に書きそうなバリデーションです。

(after内の処理にいい例がなかったので、ちょっと特殊な例になってしまいましたがあまり気にしないでください)

    public function login(Request $request) {
        $validator = \Validator::make($request->all(), [
            'email'    => 'required|string|max:64',
            'password' => 'required|string|max:64',
        ]);

        $validator->after(function ($validator) {
            $user = \App\User::where('name', $this->input('name'))->first();

            // 同名ユーザが存在して、ログイン中ユーザと同ユーザであればエラー
            if(null !== $user && $this->user()->id === $user->id){
                $validator->errors()->add('name', '既にログインしています。');
            }
        });

        if ($validator->fails()) {
            return response()->json([
                'status' => 400,
                'errors' => $validator->errors()
            ], 400);
        }

        // request is valid ......
    }

ほぼ全てのPOST系コントローラーにこれと同等な処理が書かれていました。 一律でエラー時のレスポンス構造を設定する方法などもありそうですが、開発初期だったためこの書き方で統一していました。

コントローラーにバリデーションを書いてしまうと、常にコントローラーがバリデーションについて責任を持たなければなりませんし、約10行近い処理がメソッドを占領してしまいます。

これは非常によくありません。

そこでバリデーションをFormRequestに置き換えることになりました。

FormRequestを作成する

まずArtisanCLIコマンドのmake:requestを使用して、FormRequestのファイルを作成しましょう。 今回はログイン時のバリデーションをしたいのでLoginRequestとしましょう。

php artisan make:request LoginRequest

app/Http/Requests以下にLoginRequest.phpファイルが作成されます。 このファイルに先ほどコントローラーに書いていたバリデーションを移植します。

まず作成されたファイルはこのようになっています。

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

ここで一番気をつけるべきなのはpublic function authorize()です。 デフォルトでfalseを返すようになっていますが、特にアクセス権限などを求めないリクエストであればtrueを返すように書き換えてください。

バリデーションルールを定義

ではバリデーションを移植していきましょう。 public funciton rules()がありますね。 ここにバリデーションのルールを書きます。

    public function rules()
    {
        return [
            'email'    => 'required|string|max:64',
            'password' => 'required|string|max:64',
        ];
    }

ルールはこれでOKです。

ですがこのままではバリデーションエラーが発生した時にHTMLを返してしまいますので、JSONを返すようにメソッドをオーバーライドしていきます。

レスポンスを定義

function failedValidation(Validator $validator)を定義します。 ここではバリデーションを走らせて失敗した後のvalidatorが引数に渡されます。 ですから$validator->errors()などを駆使してレスポンスのjsonを生成しましょう。 そして戻り値にはHttpResponseException(response)を返します。

    protected function failedValidation(Validator $validator) {
        $res = response()->json([
            'status' => 400,
            'errors' => $validator->errors(),
        ], 400);
        throw new HttpResponseException($res);
    }

afterなどの追加処理を定義

$validator->after()も移植しましょう。 function withValidator(Validator $validator)を定義します。 ここではバリデーションが実行される前に呼び出されるので、validatorを使って何か事前処理を書きたい場合に使います。

    public function withValidator(Validator $validator) {
        $validator->after(function ($validator) {
            $user = \App\User::where('name', $this->input('name'))->first();

            // 同名ユーザが存在して、ログイン中ユーザと同ユーザであればエラー
            if(null !== $user && $this->user()->id === $user->id){
                $validator->errors()->add('name', '既にログインしています。');
            }
        });
    }

完成したFormRequest

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;


class LoginRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'email'    => 'required|string|max:64',
            'password' => 'required|string|max:64',
        ];
    }

    protected function failedValidation(Validator $validator) {
        $res = response()->json([
            'status' => 400,
            'errors' => $validator->errors(),
        ], 400);
        throw new HttpResponseException($res);
    }

    public function withValidator(Validator $validator) {
        $validator->after(function ($validator) {
            $user = \App\User::where('name', $this->input('name'))->first();

            // 同名ユーザが存在して、ログイン中ユーザと同ユーザであればエラー
            if(null !== $user && $this->user()->id === $user->id){
                $validator->errors()->add('name', '既にログインしています。');
            }
        });
    }
}

FormRequestを組み込む

作成したLoginRequestをコントローラーに組み込んでバリデーションに使用してもらいましょう。 コントローラーメソッドの引数にあったRequestLoginRequestに変更するだけです。

use App\Http\Requests\LoginRequest; // インポートを忘れずに


    public function login(LoginRequest $request) {
        // request is valid ......
    }

なんということでしょう! あの煩雑で責務の混ざり合った見るたびに嫌気のするコントローラーが、こんなにも開放的で責務の分離された美しいメソッドに生まれ変わりました! これには開発者さんも幸福感を隠しきれません。

まとめ

Controller内のバリデーションをFormRequestに抽出するだけ多くのメリットをもたらします。 何か事情がなければ最初からFormRequestを使った方がいいですね。

プロジェクトで途中からFormRequestを使うようにした場合やLaravel初学者が多い場合は、一律でFormRequestを使うように周知しておくのをお勧めします。 もし抽出後の綺麗なコントローラーに誰かがバリデーションをそのまま追加してしまった日には

なんということをしてくれたのでしょう! あの開放的で責務の分離された美しいメソッドが、煩雑で責務の混ざり合った見るたびに嫌気のするコントローラーに逆戻りです! これには開発者さんも般若の顔を隠しきれません。

と叫ぶことになってしまいますからお気をつけください。

参考

Raspbianの固定ローカルネットワークアドレスの設定

IPの設定

まず/etc/dhcpcd.confを開きます。

ここで固定IPを設定します。

interface eth0
static ip_address=192.168.10.33/24
static routers=192.168.10.1
static domain_name_servers=192.168.10.1 8.8.8.8

ip_addressは設定したいRaspberryPiのIPアドレスrouterは同ネットワークのルーターIPアドレスdomain_name_serversDNSと呼ばれるもので名前解決するサーバーのIPアドレス

設定が通らない時はいくつかのパターンがあります。

  • ルーターIPアドレスが間違っている。 -サブネットが間違っている。
  • 物理的にネットワークに繋がっていない。
  • domain_name_serversの指定が間違っていたりして、/etc/resolve.confに正常な設定がされない。

確認

まずはネットワークが設定されているか確かめましょう。 適当な所でCtrl+cを入力して終了しましょう。

$ ping 8.8.8.8

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=49 time=10.9 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=49 time=5.33 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=49 time=5.52 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=49 time=5.46 ms

^C
--- 8.8.8.8 ping statistics ---

もしここでping: icmp open socket: Operation not permittedといったエラーが発生した場合はpingコマンドの設定をしましょう。

$ sudo chmod u+s /bin/ping

これで繋がります。

SSH

RaspberryPi3にSSHする場合、専用の設定をしておかなければSSHができません。

これについては簡潔な外部記事(1分程度)を貼っておきますのでそちらをご覧ください。

まとめ

以上で設定は終了です。

同じネットワークに属す別のPCからSSHしてみましょう。 ユーザー名など変更していないのであればpiのままです。

$ ssh pi@192.168.10.33

yes/noの質問が出たらyesを入力しておけば大丈夫です。