Web系エンジニアに転職して約2年働いて退職した話

はじめに

2019年に転職してWeb系のエンジニアとなり、約2年働いた後、今月で退職しました。

本エントリではその振り返りと、退職後のこれからについて触れます。

目次

振り返り

まずは、退職した企業の振り返りから。

どんな業態の企業だったか

Webサービスを複数持ち、自社の正社員エンジニアにてそれらの開発・保守を行っている企業です。

よくWeb系自社開発企業、受託、SESなどと分類されることがありますが、そのうちのひとつ目にあたる企業になるかと思います。

どんな仕事内容だったか

入社後、最初の4ヶ月はLaravelとVue.jsによるWebアプリケーションを開発するチームにて社内向けやtoB向けの機能追加や改善などを行いました。

その後はSREチームへ異動となり、以降は自社の全サービスのインフラ(AWS)構築やCI/CD整備などを担当しました。

働いてみての所感

とても恵まれた環境で、この企業でWeb系のエンジニアとして働けたことは本当に良かったと思っています。

具体的には以下の3点になります。

  • 人が良かった
  • スキルを高められる環境だった
  • 裁量があった

1. 人が良かった

全体的に穏和な人が多く、約2年働いた中でギスギスした雰囲気の中で仕事をするようなことはありませんでした。

社内のエンジニアは書籍Team GeekにあるHRT

  • Humility(謙虚)
  • Respect(尊敬)
  • Trust(信頼)

を大事にしており、こうした点は日々のコードレビューでの指摘、Slackでのテキストベースのやり取り、障害発生後の振り返りの場(ポストモーテム)での各エンジニアの行動にも表れていました。

責めたり、詰めたりするような行動は忌避されていました。

2. スキルを高められる環境だった

最初に配属されたWebアプリの開発チームでは毎日のようにコードレビューを受けていましたが、LaravelやVue.jsについて深い知識を持ったエンジニアからの的確な指摘により鍛えられました。

また、次のSREチームでも、いまは離任していますが長年のインフラ経験と最新のクラウドの知見のあるリーダーのもと、幅広い仕事を担当させてもらいました。

両チームで共通していたこととして、わからない点は遠慮無く聞くことのできる雰囲気が醸成されていて、安心感を持って日々スキルを高めていくことができました。

3. 裁量があった

個々のレビュー指摘はしっかりありますが、一方で案件の方針や進め方に関しては裁量を与えられ、自由にやらせてもらえていた気がします。

こちらの意見や提案も採り入れてもらえました。

創意工夫が活かせる点は、やりがいをもって仕事に臨むことができました。

退職に至った理由

このような恵まれた環境で働くことができていたのですが、2年が経過し様々な環境の変化のある中で、これからを考えて退職することにしました。

理由はいくつかあるのですが、最も大きいものとして、自分の目指す方向(SRE)について、さらに高度な知見・スキルを身に付けて組織を牽引できるような人間へとなりたいという思いがあり、そのためにはより先進的な取り組みを行っている組織に飛び込むのが一番だという考えに至ったからです。

この辺りは話し始めると非常に長くなるので、私のことを知ってる方は興味があればオンライン飲みにでも誘ってください。色々語らせていただきます。

退職後について

現状よりも一段規模の大きいWeb系の自社開発企業にて、来月からSREの一員として働きます。

その企業はSREに限らず、非常にレベルの高いエンジニアが多数在籍しているので、いまの自分がやっていけるかどうか不安もありますが、約2年前にWeb系にキャリアチェンジした時の気持ちを思い出して、精一杯頑張っていきたいと思います。

最後に

今回の突然の転職では色々な方にご迷惑をお掛けしましたが、応援もしていただき、本当にありがとうございました。

これからもどうぞよろしくお願い致します。

AWSの予測請求額を通知して想定外の費用発生を防ぐ

はじめに

昨年末、勉強のために個人のAWSアカウントにとあるAWSリソースを一時的に作成しました。

その後、このリソースは不要になったのですが、削除することを忘れ、そのまま年を越してしまいました。

年末年始はAWSを触ることも無くのんびりと過ごしていたのですが、1月3日にAWSから以下のメールが届きました。

月末の予測請求額が28ドルであることを通知するメール

「28ドル!」

これは実際に28ドルの請求が発生したわけでなく、いまのAWS利用のペースで月末を迎えると今月の請求額が28ドルになることを予測して通知してくれたメールです。

このメールを受けて私は自分のAWSアカウント内に放置している課金対象のリソースが無いかを調べ、その存在に気付いて削除しました・・・。

AWS Budgetsについて

上記のメールはAWS Budgetsという機能により送信されたものです。

AWSアカウントを個人で運用して勉強中の初心者の中には、私と同様に不要リソースを削除し忘れた経験のある方もいるかと思います。

本記事ではAWS Budgetsを使って、請求額の予測が一定以上になったら通知する方法を解説します。

非常に簡単ですので、こうした設定を行なっていない人はぜひやってみてください。

AWSのベストプラクティスであるAWS Well-Achitectedにおいても、想定外の費用発生を通知するよう設定しておくことはコスト管理の基本であるといった旨が書かれています。

コントロールと通知: コスト管理を導入する際の一般的な最初のステップは、ポリシー外のコストまたは使用量イベントが発生した場合に通知するように設定することです。

目次

設定手順

今回はAWSマネジメントコンソールを使った設定方法を解説します。

1. AWS Budgetsの画面で予算を作成

検索欄にbudgetsなどを入力して、AWS Budgetsの画面を開きます。

マネジメントコンソールでbudgetsを入力

AWS Budgetsの画面が開いたら、予算を作成ボタンを押します。

予算を作成ボタンを押す

2. コスト予算の選択

コスト予算を選択し、予算を設定ボタンを押します。

コスト予算を選択し、予算を設定ボタンを押す

3. 予算を設定

まず、予算名と期間を設定します。

予算名と期間を設定

  • 名前 : 予算に付ける名前です。ここで付けた名前がメールの件名や本文に含まれることになります。
  • 間隔 : 月別を選択します。
  • Budgets effective dates : 定期予算を選択します。
  • 開始月 : 今月を設定します。

次に、毎月の予算を指定します。

固定10ドルを入力

固定を選択し、予算額は適当な値を入力してください。

ここでは10ドルとしておきます。

以下の追加の予算パラメータでは、予測請求額の計算に含めるAWSのサービスの種類やリージョンなどを絞ることができますが、今回はAWSアカウント全体での予測請求額で通知をしたいので、デフォルトのままとします。

追加の予算パラメータはデフォルトのまま

ここまで来たら、画面右下のしきい値を設定するボタンを押します。

しきい値を設定するボタンを押す

4. しきい値と通知先を設定する

続いて、しきい値を設定します。

予測コストを選択し、しきい値を100%に設定

予測コストを選択し、アラートのしきい値を100%とします。

こうすることで、月末の予測請求額が予算額100%(ここでは10ドル)を超過する見込みであると通知されるようになります。

さらに通知の宛先とするメールアドレスを入力します。

メールアドレスを入力

最後に予算の確認ボタンを押し、次画面で確認ボタンを押せば設定完了です。

終わりに

これで月の請求額が10ドルを超える見込みになると、月の途中でも通知メールが届くようになりました。

なお、AWS BudgetsではAWS Chatbotと連携してSlackに通知することも可能ですが、メール通知よりも少し設定手順が多いので本記事では割愛します。

Slackに通知してみたい方は以下の記事を参考にしてください。

Web系エンジニア2年目による1年間の振り返り

はじめに

昨年2020年の1年間は、私がWeb系のエンジニアになって9ヶ月目〜1年8ヶ月目にあたります。

おおよそエンジニア2年目にあたるこの期間を振り返るとともに、2021年の抱負について考えたいと思います。

目次

本業

本業では引き続きSREチームに所属し、CI/CDの整備やAWSでのインフラ構築・管理などを行いました。

様々なタスクをこなしましたが、2年目として特に印象に残ったのが以下の2案件です。

  1. 既存サービスのインフラ刷新
  2. Go + Lambda + SQSのマイクロサービス新規構築

既存サービスのインフラ刷新

3年以上運営されている既存サービスのインフラ保守を、新たに私が所属するSREチームで引き取ることになりました。

この既存サービスのインフラは、SREが管理する他のインフラと比較して設計的に劣後する箇所が見受けられ、またデプロイが手動であるなど、いくつかの課題を抱えていました。

そこで既存のインフラのままで保守しつつ、並行して4ヶ月かけて新規インフラを構築し、既存から新規へ移行させました。

詳細は伏せますが、主に以下の改善を行いました。

  • セキュリティの向上
  • Terraformの導入
  • デプロイの自動化

移行は無事完了し、現在は新インフラが問題無く稼働しています。

この案件の推進に関しては私がリーダーとして一任され、チームのもう1人のメンバーと二人三脚で進めました。

私はプレイングマネージャー的にプロジェクト管理をしつつ、自らも手を動かしてインフラ構築を行いました。

もう1人のメンバーと相互に補完し合って案件を推進することができ、インフラ系のチーム開発として非常に良い経験を得ることができました。

Go + Lambda + SQSのマイクロサービス新規構築

とあるWebサービスに機能追加が必要となり、検討した結果、Lambdaでマイクロサービスを構築することにしました。

Lambda上で動くGoのコードもSREで書きました。

実務でGoを書くのはこれが初めてで苦労も多かったですが、非常に勉強になりました。

Goは引き続き書いていきたいと思います。

社外活動

JAWS-UG(Japan AWS User Group)というAWSのユーザーグループがあります。

JAWS-UGには目的・地域別に分かれた50以上の支部が存在するのですが、私はその中でAWS初心者向けに特化した「初心者支部」に運営メンバーとして参加しています。

2020年には10回以上のオンラインイベントを開催し、その規模も毎回参加者100人以上となっています。

こうした各イベントにおいて、私は5人以上いる運営メンバーと役割分担をしながら、時には司会進行をしたり、時にはハンズオンイベントの講師サポート(参加者からの質問対応など)をしたりしました。

登壇

前述のJAWS-UGのオンラインイベントで、計2回登壇しました。

JAWS SONIC 2020

1つ目はJAWS SONIC 2020という24時間イベントです。

登壇テーマは、AWSがクラウドアーキテクチャのベストプラクティスとして文書で公開しているAWS Well-Achitectedとしました。

非常に深いテーマですが、初心者支部代表として、初心者にもわかりやすい・親しみやすい内容となるよう心掛けました。

youtu.be

JAWS-UG 初心者支部 & 大分支部

2つ目はJAWS-UG初心者支部と大分支部でのコラボ開催となったAWS Amplifyをテーマとした勉強会です。

実務では全く触ったことのないAmplifyでしたが、先に登壇を宣言し、そこから発表当日までに内容を勉強し資料を作るという締め切り駆動型で臨みました。

プレッシャーはありましたが、未知のAWSサービスに触れることで、技術の幅が広がりました。

技術記事投稿

技術記事をQiitaでは14本、Zennでは5本投稿しました。

Qiitaではデイリートレンド入りすることが3回ほどありましたが、いずれも小ネタで、はてなブックマークなどで話題となるような技術記事までは書くことができませんでした。

2021年はより気合の入った記事を書いて、そうした話題になれたらと思います。

執筆活動

書籍ではありませんが、Techpitというプログラミング学習プラットフォームで、テキストベースの教材を3本執筆、公開しました。

それぞれ字数にすると50万字程度はあるので、技術書一冊(200〜300ページ)ほどのボリュームになるかと思います。

そのため、執筆には本業の合間を縫って、かなりの時間をかけました。

そうした苦労の甲斐もあってか、多くの人に手に取ってもらえ、またサイトでのレビューでも平均4.7点〜5.0点(5点満点評価)と高い評価をいただくことができました。

その他、執筆に際してはできるだけ正確な解説となるよう公式のドキュメントを読み込むなど私自身も非常に勉強になりましたので、アウトプットのひとつのかたちとして取り組んで良かったと思います。

Twitter

フォロワー数は年初に2,400人だったところ、4,200人まで増えました。

1,800人の増となります。

Twitterを通じてリアルで知り合えた人も増えたので、今後も地道に続けていきたいと思います。

2021年の抱負と最後に

2021年の抱負としては、これまでのような活動を続けつつ、本業であるSREに関して、より専門的で高度な知見・スキルを身に付け、更にプロダクトに貢献できる人材になりたいと考えています。

そのために思い切ったこともしようと計画もしていますが、その詳細はまた別の機会に・・・(以下、後日に追記しました)。

では、本年もどうぞよろしくお願い致します。

Laravel + CircleCI + AWSでCI/CDを学ぶチュートリアルを公開しました

ci/cd

はじめに

Laravel + Vue.jsのサンプルアプリケーションをCircleCIを使ってAWS(EC2)にデプロイする、CI/CDパイプライン構築を学ぶチュートリアルをTechpitで公開しました。

このチュートリアルはどんな人に向いているか

以下のようなプログラミング学習者をターゲットとしています。

  • Herokuにデプロイはできるけれど、AWSへのデプロイはまだという方
  • AWSへ手動でデプロイはできるけれど、自動デプロイさせる方法がわからないという方

既にエンジニアとして実務に付いている方でも「職場にCI/CD環境はあって利用はしているけれど自分自身でゼロから構築したことは無い」という方であれば、効率的に学べる内容になっているのではと思います。

このチュートリアルを終えることで身に付く知識

チュートリアルでは以下について段階的に学べる構成となっています。

  • CircleCIの基本的な使い方(そもそもの構文やキャッシュの使い方など)

  • Laravelでのテストの書き方の初歩的な知識

  • EC2 + RDSでLaravel + Vue.jsアプリケーションを動かすための環境構築方法(nginx, PHP-FPMの設定方法など)

  • CircleCIからEC2にSSHログインしてデプロイさせるための方法(SSHキーの取り扱いなど)

  • AWSのデプロイサービスであるCodeDeployを使ったデプロイ方法とその結果をSlack通知する方法

  • AWSのCI/CD関連サービスであるCodePipeline/CodeBuild/CodeDeployを組み合わせてデプロイする方法

書いた人

昨年の9月からSREチームに配属になって色々なタスクを経験したのですが、その中で「とあるレガシーなアプリケーションをEC2に自動デプロイさせるためのCI/CDパイプラインを構築する」というものがありました。

職場の主要なアプリケーションはCI/CDが整備されていますが、そのレガシーアプリケーションは未だに手動デプロイだったのです。

そこでCircleCIやCodeDeployなどに触れ、得られた知識を元に今回のチュートリアルを書きました。

終わりに

4月の頭から執筆を始め、時々Twitterで途中経過報告をしたのですが結構反響が大きかったです。

AWSやクラスメソッドの方にいいね・リツイートされたり・・・。

だからといって、その方々がこのチュートリアルをオススメしているわけでは決してありませんが(当然中身は知らないわけだし)、執筆と終わりの見えないスクショ作業を続ける上で大変励みになりました。

CI/CDの構築方法を学びたい、という方はぜひ手に取ってもらえればと思います。

全7章構成ですが、0章から1章の途中ぐらいまでは無料で公開されています。気に入った方はその続きもぜひ。

【GitHub Actions】S3にキャッシュするアクションをリリースしました

GitHub Action - actions-s3-cache

はじめに

GitHub Actionsで、キャッシュをS3に保存するアクションactions-s3-cacheをリリースしました。

github.com

GitHub ActionsにおいてCI/CDを行う際にパッケージインストールやビルドを実行する場面があるかと思いますが、それらの結果をキャッシュするためのアクションとなります。

目次

公式のキャッシュ機能との比較

安定性

GitHub Actions公式のキャッシュ機能actions/cacheは、現在以下の問題を抱えているようです。

  • Pull Requestでコケた時にRe-run jobsするとactions/cacheアクションが正常に動作しない
  • actions/cacheアクションは時折キャッシュの取得に失敗することがある

GitHub Actionsの知見ご紹介 - Masteries

この点に関して、今回公開したアクションは安定しているのではと思います。

ジョブ異常終了時の挙動

公式のキャッシュは以下の仕様となっています。

  • 使用するステップを記述すると、そこがキャッシュをリストアするステップとなるが、キャッシュの保存に関しては、actions/cacheのステップが存在するジョブの最後にPost + actions/cacheというステップ名で実行される
  • ジョブが正常終了しなかった場合は、キャッシュを保存するステップは実行されない

つまり、

# 略
    steps:
      # 略
      - name: cache composer
        uses: actions/cache@v1.1.2
        with:
          path: vendor
          key: composer-v1-${{ hashFiles('composer.lock') }}
      - run: composer install -n –prefer-dist
      - run: vendor/bin/phpunit # テスト

のように、1ジョブ内で

  1. 公式のキャッシュを使用
  2. パッケージのインストール
  3. テストの実行

を実行する場合、初回のテストが通るまでインストールしたパッケージがキャッシュされることはありません。

actions-s3-cacheでは、後続のステップでエラーが発生してジョブが異常終了した時でも、キャッシュの保存を行います。

  • キャッシュを探す
  • 無ければパッケージをインストールする or ビルドする
  • キャッシュを保存する

という一連の流れを、アクション使用の1ステップ内で実行しているためです。

キャッシュヒット率

公式のキャッシュはGitHubリポジトリごとにキャッシュの保存容量が5GBあるそうです。

しかし、実際に使っていると5GBをまだ超過していなさそうなのに、キャッシュミスが発生するケースが見受けられました。

私の職場のとあるリポジトリについて、AさんがActionsでテストをしてキャッシュが保存された後、同じに日にBさんがテストをしたら、キャッシュのキーは同じなのにキャッシュミスになることがありました。

actions-s3-cacheではキャッシュのキーが合えば確実にキャッシュヒットする、はずです。

複数のディレクトリ・ファイルのキャッシュ可否

公式のキャッシュは、キャッシュ対象としてひとつのディレクトリしか指定できません。

また、ファイル名を指定してキャッシュすることができません。

利用シーンは限られるかもしれませんが、actions-s3-cacheでは複数のディレクトリとファイルの組み合わせをまとめてキャッシュ可能です。

使い方

例として、以下のように記述するとnpm ciによって作成されたnode_modulesをS3にキャッシュし、次回以降はS3からリストアします。

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: shonansurvivors/actions-s3-cache@v1.0.1
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-1
        with:
          s3-bucket: your-s3-bucket-name # 必須
          cache-key: npm-v1-${{ hashFiles('laravel/package-lock.json') }} # 必須 ('.zip' は記述不要)
          paths: node_modules # 必須(キャッシュするディレクトリやファイル。複数ある場合はスペース区切りで記述) 
          command: npm ci # 必須(インストールやビルドのコマンドを記述)
          zip-option: -ryq # 任意 (デフォルト: -ryq)
          unzip-option: -n # 任意 (デフォルト: -n)
          working-directory: laravel # 任意 (デフォルト: ./)

事前準備としては、

  • S3バケットを作成する
  • S3バケットを読み書き可能なポリシーを持ったIAMユーザーを作成する
  • GitHubリポジトリのsettings画面で上記IAMユーザーのアクセスキーIDとのキーをsecretsに設定する

が必要となります。キャッシュですので、S3バケットにはライフサイクルポリシーを付けて一定日数でオブジェクトが削除されるようにすると良いかと思います。

以下はIAMのポリシーの例です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
            ],
            "Resource": [
                "arn:aws:s3:::your-s3-bucket-name",
                "arn:aws:s3:::your-s3-bucket-name/*"
            ]
        }
    ]
}

なお、アクションの種類には、DockerアクションとJavaScriptアクションが存在しますが、actions-s3-cacheは後者のJavaScriptアクションです。

ですので、GitHubが提供するVM環境で直接実行されます。

最後に

職場では今回のアクションの原型となる、S3キャッシュのためのシェルを作って使っているのですが、GitHubリポジトリごとにシェルをコピペしなければなりませんでした。

今回アクションとしてOSS化したことで共通的に使い回せるようになりました。

この記事を読んでいただいた方にも、このアクションを使っていただけたら幸いです。

Laravel + Vue.jsでいいね機能・フォロー機能などを作るチュートリアルを公開しました

はじめに

LaravelとVue.jsで、いいね機能やフォロー機能を持つSNS風のWebサービスを作るチュートリアルをTechpitで公開しました。

目次

執筆の経緯

Techpitでは昨年9月にLaravel x LINE Botのチュートリアルを公開しましたが、ブラウザで利用できるタイプのスタンダードなWebアプリケーションの作り方も解説したいと思っていました。

昨年末にアプリ部分をいったん仕上げ、年が明けて1月からチュートリアル記事部分の執筆を開始し、3月に完成しました。

字数は50万字以上、画像も多く使用していますので、もし本にすると400ページぐらいのボリュームになるかと思います。

執筆にあたって心掛けたこと

チュートリアルの執筆にあたって以下を心掛けました。

  1. Webアプリの完成だけを目的とせず様々な知識を学べるようにする

  2. プログラミング初心者が知りたそうなWebサービスの「よくある機能」の作り方を盛り込む

  3. 基本的な機能から応用的な機能まで段階を踏んで学ぶ構成にする

  4. 認証機能を敢えてコマンド一発で作らない

  5. メール送信に関しては商用のWebサービスでも利用されるメールサービスを使う

  6. Laravelを使っていると普段目にしないSQLを確認・改善するプロセスを盛り込む

順に解説します。

1. Webアプリの完成そのものを目的とせず様々な知識を学べるようにする

チュートリアルでは、ただWebアプリの完成を目指すのではなく、途中途中に登場するコードとその解説を通じて、様々な知識を得られるようにしました。

本チュートリアルでは、Webアプリ作りを通じて、以下の知識に触れられるようになっています。

  • プログラミング一般
    • DIの初歩 / N+1問題
  • PHP
    • 三項演算子 / null合体演算子 / 型キャスト / 型宣言 / コールバック / クロージャ / トレイト
  • Laravel
    • リソースルート / authミドルウェア / CSRF保護 / フォームリクエスト / バリデーション / Blade / LaravelMix / 認証・認可 / ポリシー / コレクション / 各種ヘルパ関数 / メール / Eloquent ORM / データベースマイグレーション / リレーション / アクセサ / Eagerロード / Laravel Socailite / Laravel Debugbar
  • Vue.js
    • 単方向のデータフロー / Vue Tags Input
  • メール関連
    • Mailhog / Sendgrid

DI(Dependency Injection/依存性の注入)、型宣言、トレイトなどPHP初心者にはやや敷居が高いと思われる内容にも、知識の入り口として少し触れるようにしています。

2. プログラミング初心者が知りたそうなWebアプリの「よくある機能」の作り方を盛り込む

このチュートリアルで作成するWebアプリそのものはシンプルな記事投稿型サービスとなっています。

その代わりに、

  • いいね機能
  • フォロー機能
  • 記事へのタグ付け機能
  • ソーシャルログイン(Googleログイン)

といった、Webサービスでよく見られる機能を盛り込んでいます。

汎用的な機能ですので、プログラミング初心者の方が今後自作するWebアプリ、ポートフォリオに追加機能として組み込むこともできるのではないかと思います。

また、機能自体も、複数テーブルの多対多のリレーションや非同期通信、外部ライブラリの利用などの要素があり、知識やスキルのアップに繋がるのではと思います。

3. 基本的な機能から応用的な機能まで段階を踏んで学ぶ構成にする

一度に多くの情報を解説されても理解が追いつかない、という自身の学習経験から、

  • ひとつの要素を解説し、これを実装してから次の要素に進む

という構成を心掛けました。

例えば、第1章の「記事一覧を作ろう」では記事一覧画面を作成しますが、1章の前半では、コントローラーからビューに渡す記事一覧の情報をデータベースから取得せず、ダミーの固定データとしています。

具体的には、

  • コントローラーが初登場するパートでは、ビューにデータを渡すというコントローラーの役割のひとつを理解してもらう
  • その次のパートでは、ビューを作成し、ダミーの固定データを表示する
  • さらにその次のパートで、初めてデータベースに記事テーブルを作成する

といった具合です。

Laravel、というかMVCフレームワークそのものを初めて触る初心者でも、つまづくことなく、ひとつひとつの要素を学んでいけるようになっています。

なお、チュートリアルの後半では、基礎を理解している前提でもう少しテンポアップして解説を進めますので、説明がゆっくり過ぎてまどろっこしい、といったことも無いかと思います。

いま説明したのは1つの章の構成の話ですが、チュートリアル全体としても、

  • 前半の6章まではLaravelの知識だけで開発できる
  • 中盤を過ぎた7章の「いいね機能」からは応用編としてフロントエンドにVue.jsを導入

といった構成になっています。

チュートリアルの章立て

  • 0章:はじめに
  • 1章:記事一覧を作ろう
  • 2章:ユーザー登録機能を作ろう
  • 3章:ログイン機能を作ろう
  • 4章:記事投稿機能を作ろう
  • 5章:記事更新・削除・詳細表示を作ろう
  • 6章:メールを使ったパスワード再設定機能を作ろう
  • === ここまでLaravelのみ ===
  • === ここからVue.jsも登場 ===
  • 7章:いいね機能を作ろう
  • 8章:タグ機能を作ろう
  • 9章:フォロー機能とユーザーページ機能を作ろう
  • 10章:Googleアカウントを使ったユーザー登録・ログイン機能を作ろう
  • 11章:デバッグバーを使ってSQLを改善しよう
  • 12章:作ったWebサービスをインターネットに公開しよう

4. 認証機能を敢えてコマンド一発で作らない

もともとLaravelは、(laravel/uiというライブラリを別途インストールすれば)ユーザー登録やログインといった認証関連の機能をコマンド一発で作成できます。

これはこれでとても便利なのですが、この方法だけでしか認証機能を作った経験が無い場合、初心者がいざ自分の作ろうとするWebアプリに合わせて認証機能をカスタマイズしようと思った時に、どのコードから手を付ければ良いのか分からないのではないでしょうか?

そこで、本チュートリアルでは認証機能を敢えてコマンド一発で作らずに、フレームワークのコードを読みながら順に作成していくようにしました。

本チュートリアルを通じて、フレームワークのコードを読む、という実務で求められる行為にも少し慣れることができるのではないかと思います。

5. メール送信に関しては商用のWebサービスでも利用されるメールサービスを使う

Laravel初心者で、Laravelからメール送信を行ったことがある人は、普段から馴染みのあるGmailのセキュリティ設定を下げて利用したことが多いのではないでしょうか。

本チュートリアルでは、商用のWebサービスでも利用されている、Sendgridというメールサービスの無料枠を使ってメール送信を行う方法を解説しています。

今後、Laravelで個人開発のWebサービスなどを作る際に、メール送信機能を組む込む上で、この知識が役に立つかと思います。

6. Laravelを使っていると普段目にしないSQLを確認・改善するプロセスを盛り込む

LaravelではデータベースのアクセスにORM(Object-relational mapping)が使用されており、SQLを書くことなく、データベースのテーブルにアクセスできます。

ただし、実務ではどのようにSQLが発行されているか意識して開発する必要があります。

本チュートリアルでは、Laravelのデバッグ機能を使用して実際に発行されているSQLを確認するとともに、非効率な部分(N+1問題)を改善してLaravelのレスポンスを向上させるプロセスを学びます。

チュートリアルへの感想

チュートリアルを公開してまだ1週間ですが、さっそく感想をブログやTwitterで報告してくれている方がいますので、一部掲載させていただきます。

ありがとうございます!

ちなみにRails、Laravel、Python、Vue.jsなど含め、Techpit教材はかなり漁ってきました。その中でもかなりおすすめの教材です。

初学者がポートフォリオ作成で躓く(私含め)であろうミドルウェア、リクエスト、認可、ポリシー、さらにはフロンドエンドフレームワーク(Vue.js)との連携まで、実際にアプリを作りながら体験できます。

【感想】Laravel(+Vue.js)でSNS風Webサービスを作ろう!を終えて

終わりに

以上、Laravel x Vue.jsのチュートリアルの解説でした。

ぜひ、このチュートリアルを手に取ってLaravelを学び、個人開発や就職のためのポートフォリオ作りなどに活かしていただければと思います!

【初心者向け】Laravelテストチュートリアル

Laravel6

はじめに

「ようこそ・・・『テストの世界』へ・・・」

本記事では、テストコードをまだ書いたことのないプログラミング初学者向けに

「今日からテストを書いてみようかな」

と思ってもらえるよう、チュートリアル形式で簡単なテストの流れを説明します。

題材はLaravelですが、他のフレームワークでも同じようなことはできると思います。

注:本記事は、以前に私がQiitaに投稿したLaravel5.8のテストチュートリアルをLaravel6で動作するよう改訂したものです。

目次

前提

本記事の対象者

  • Laravel初心者で、
    • テストをまだ書いたことの無い人
    • テストで何ができるのか知らない人
    • テストに興味はあるが忙しくてどう書けば良いのかまだ調べられていない人

本記事で取り扱うこと

  • ごく簡単なHTTPテストの書き方

本記事で取り扱わないこと

  • テストの全般的な話(利点、注意点など)
  • テストケースの作り方
  • CIツールによるテスト自動化

環境

  • Laravel 6.4.x

簡単な画面(HTTPレスポンス)のテスト

まずは、Laravelを触ったことのある人なら一度は見たことのある?Welcome画面をテストしてみます。

これが正常に表示されることを、目視ではなくテストコードで確認してみましょう。

Welcome画面

Laravelをインストールすると、既にtests/Feature/ExampleTest.phpという、テストコードのサンプルが存在します。

tests/Feature/ExampleTest.php

<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

このExampleTestは、Tests\TestCaseを継承しており、テストに関する様々なメソッドが使えます。

$response = $this->get('/')で、'/'にアクセスした(GETリクエストした)結果のレスポンス(注1)が$responseに代入されます。

注1: 正確には\Illuminate\Foundation\Testing\TestResponseが代入されます。

そして、$response->assertStatus(200)で、そのレスポンスが正常であること(ステータスコードが200 OKであること)をチェックしています。

ステータスコードについては以下を参考にしてください。

このExampleTestを実行するには、Laravelのルートフォルダで以下コマンドを実行してください。

$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.php

すると、以下の結果が表示されました。

PHPUnit 8.4.3 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 405 ms, Memory: 16.00 MB

OK (1 test, 1 assertion)

OK、と表示されています。

つまり、'/'へアクセスした(GETリクエストした)結果、正常にレスポンスが返ってきた(HTTPレスポンスステータスコードが200 OKだった)ということです。

これをテストコードで確認することができました。

ビューのテスト

ただ、このテストでわかったのは、正常にレスポンスが返ってきた、ということまでです。

Welcome画面が表示されたのかどうかまでテストできていません。

現状のルーティングを見ると、'/'へアクセスすると、'welcome'というビューが表示されることがわかります。

routes/web.php

<?php

// 略

Route::get('/', function () {
    return view('welcome');
});

welcomeというビューは、具体的にはresources/views/welcome.blade.phpというビューのテンプレートです。

このresources/views/welcome.blade.phpこそがあのWelcome画面なので、このテンプレートが使われているかどうかをテストで確認してみます。

tests/Feature/ExampleTest.phpに以下のコードを追加します。

tests/Feature/ExampleTest.php

<?php

// 略

public function testBasicTest()
{
    $response = $this->get('/');

    $response->assertStatus(200)
        ->assertViewIs('welcome'); // 追加
}

このようにassertViewIsメソッドで、どんなビューが使われたのかをテストすることができます。

修正後のExampleTestを実行してみます。

$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.php
// 略
OK (1 test, 2 assertions)

こちらも結果はOKとなりました。

あのWelcome画面が表示されていることを、テストコードで確認することができました。

さらにassertSeeメソッドを使うと、表示(注2)されている文字列もテストできます。

注2: 正確にはhtml内にその文字列が含まれているかをテストします。htmlのタグなども確認の対象になります。

Welcome画面にLaravelの文字が表示されていることを、テストコードで確認してみましょう。

tests/Feature/ExampleTest.phpに以下のコードを追加します。

tests/Feature/ExampleTest.php

<?php

// 略

public function testBasicTest()
{
    $response = $this->get('/');

    $response->assertStatus(200)
        ->assertViewIs('welcome')
        ->assertSee('Laravel'); // 追加
}

修正後のExampleTestを実行してみます。

$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.php
// 略
OK (1 test, 3 assertions)

こちらも結果はOKとなりました!

ログイン中かどうかを絡めたテスト

ここから先は、ユーザーがログイン中かそうでないかによって結果が異なる画面をテストしていきます。

laravel/uiライブラリからユーザー登録画面やログイン画面等を作成できるので、これを実行します。

$ php artisan migrate
$ composer require laravel/ui
$ php artisan ui vue --auth
$ npm install
$ npm run dev

上記コマンドの詳細については、以下の記事を参考にしてください。

Welcome画面にユーザー登録画面とログイン画面へのリンクが追加されました。

LoginとRegisterが追加されたWelcome画面

以下、テストではなく人力でユーザー登録画面にアクセスし、ユーザー登録を行なっています。

Register画面

ユーザー登録が完了すると、以下のホーム画面へ遷移します。なお、ログイン画面からログインした場合も、このホーム画面へ遷移します。

ホーム画面

ホーム画面は、ログイン中の時のみ表示されます。

ログインしていない状態で直接/homeへアクセスすると、ホーム画面は表示されず、ログイン画面へリダイレクトされます。

この「ホーム画面は、ログイン中の時のみ表示される」ということをテストコードで確認してみましょう。

ホーム画面の表示は、php artisan make:authコマンドによって作成された、app\Http\Controlles\HomeControllerで行われています。

このHomeControllerをテストするためのHomeControllerTestを作成します。

以下コマンドを実行すると、テストの雛形を作成できます。

$ php artisan make:test HomeControllerTest

作成されたテストの雛形の内容は、以下になります。

tests/Feature/HomeControllerTest.php

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class HomeControllerTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testExample()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

このtests/Feature/HomeControllerTest.phpを以下の通り編集します。

tests/Feature/HomeControllerTest.php

<?php

// 略

public function testExample()
{
    $response = $this->get('/home'); // 変更(ホーム画面のパスに変更)

    $response->assertStatus(200)
        ->assertViewIs('home') // 追加(ここでの'home'は、ホーム画面で使われているビュー名)
        ->assertSee('You are logged in!'); // 追加(ホーム画面で表示されているメッセージ)
}

このHomeControllerTestを、以下コマンドで実行します。

$ ./vendor/bin/phpunit ./tests/Feature/HomeControllerTest.php

すると、以下の通り、OKではなくFAILURES!となりました。

PHPUnit 8.4.3 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 1.15 seconds, Memory: 16.00 MB

There was 1 failure:

1) Tests\Feature\HomeControllerTest::testExample
Expected status code 200 but received 302.
Failed asserting that false is true.

/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:183
/var/www/tests/Feature/HomeControllerTest.php:20

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Expected status code 200 but received 302.と出力されています。

ログイン中でない状態でアクセスしたので、リダイレクトを示す302のステータスコードが返ってきた、ということです。

そこで、ログインした状態を作り出してテストしてみます。

DBには、先ほど人力でユーザー登録画面にアクセスして登録したユーザーデータが存在するので、テストではこのユーザーを使うことにします。

# select id, name from users;
 id |        name        
----+--------------------
  1 | shonansurvivors
(1 rows)

tests/Feature/HomeControllerTest.phpを以下の通り編集します。

tests/Feature/HomeControllerTest.php

<?php

namespace Tests\Feature;

use App\User; // 追加
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;


class HomeControllerTest extends TestCase
{
    public function testExample() 
    {
        $response = $this
            ->actingAs(User::find(1)) // 追加
            ->get(route('home'));

        $response->assertStatus(200)
            ->assertViewIs('home')
            ->assertSee('You are logged in!');
    }
}

actingAsメソッドで、そのユーザーとしてログイン済の状態になります。

また、User::find(1)で、DBのusersテーブルからid1であるユーザーを取ってきています。

改めてHomeControllerTestを、以下コマンドで実行します。

$ ./vendor/bin/phpunit ./tests/Feature/ExampleTest.php

すると、OKとなりました。

OK (1 test, 3 assertions)

ログイン中の状態で/homeへアクセスすると、

  • ステータスコードが200となること
  • homeというビューが使われていること
  • You are logged in!が、html中に存在すること

が確認できました。

このようにログイン中かどうかが絡む機能も、テストコードで確認できます。

テストに必要なDBのデータを自動で準備する

今回はたまたま開発環境のDBにユーザーのデータが存在し、テストではこれを利用してログインを行いましたが、 テストに必要なDBのデータ準備もコードで自動化してみます。

Laravelではテスト用のDBのデータを作るのに、ファクトリというものを使う方法があります。

database/factories/UserFactory.phpが、ユーザーデータを作るためのファクトリです。

database/factories/UserFactory.php

<?php

use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // password

        'remember_token' => Str::random(10),
    ];
});

このファクトリの詳細については本記事では触れませんが、これを使うといい感じにダミーのデータを作ってくれます。

ファクトリについてもっと知りたい方は、以下記事を参考にしてください。

このファクトリを使うよう、tests/Feature/HomeControllerTest.phpを以下の通り変更します。

tests/Feature/HomeControllerTest.php

<?php

namespace Tests\Feature;

use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;


class HomeControllerTest extends TestCase
{
    public function testExample()
    {
        $user = factory(User::class)->create(); // 変更(ファクトリでユーザーデータを作成)

        $response = $this
            ->actingAs($user) // 変更(ファクトリで作ったユーザーデータでログイン中状態を作る)
            ->get(route('home'));

        $response->assertStatus(200)
            ->assertViewIs('home')
            ->assertSee('You are logged in!');
    }
}

ファクトリで作成されたユーザーにて、ログイン中の状態を作っています。

以下コマンドでHomeControllerTestを実行すると、結果はOKとなりました。

$ ./vendor/bin/phpunit ./tests/Feature/HomeControllerTest.php
// 略
OK (1 test, 3 assertions)

ただ、これだとHomeControllerTest実行のたびにDBにダミーのユーザーデータが1件また1件...と作られてしまいます。

sample=# select id, name from users;
 id |        name        
----+--------------------
  1 | shonansurvivors
  2 | Dr. Jayce Wiegand
(2 rows)

2件目がファクトリで作られたダミーのユーザーデータです。

これに対しては、ひとつのやり方として、テスト用のDatabaseTransactionsを使うという方法があります。

これを使うと、テストの実行後にDBをロールバックし、テスト実行中にDBに作ったデータが残らなくなります。

tests/Feature/HomeControllerTest.phpで、DatabaseTransactionsを使うようにします。

tests/Feature/HomeControllerTest.php

<?php

namespace Tests\Feature;

use App\User;
use Illuminate\Foundation\Testing\DatabaseTransactions; // 追加
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;


class HomeControllerTest extends TestCase
{
    use DatabaseTransactions; // 追加

    public function testExample()
    {
        // 以下変更点なしにつき省略

以下コマンドでHomeControllerTestを実行すると、結果は引き続きOKとなりますが・・・

$ ./vendor/bin/phpunit ./tests/Feature/HomeControllerTest.php
// 略
OK (1 test, 3 assertions)

テスト終了後のDBを見ると、ユーザーデータはテスト開始前時点から増えてはいません(2件のまま)でした。

sample=# select id, name from users;
 id |        name        
----+--------------------
  1 | shonansurvivors
  2 | Dr. Jayce Wiegand
(2 rows)

これでDBにダミーデータが残ることなく、何度でもテストを実行できます。

最後に

以上で、本記事のテスト体験ツアーは終了です。

テストでどのようなことができるか、イメージはつかめたでしょうか。

本記事をきっかけに、テストを書き始めていただければ幸いです。

「納期は守る」「品質も確保する」 「両方」やらなくちゃあならないってのが「エンジニア」のつらいところだな