<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Pepabo Tech Portal</title>
  <id>https://tech.pepabo.com/</id>
  <link href="https://tech.pepabo.com/"/>
  <link href="https://tech.pepabo.com/feed.xml" rel="self"/>
  <updated>2024-02-21T15:00:00+00:00</updated>
  <author>
    <name>GMO Pepabo, Inc.</name>
  </author>
  <entry>
    <title>minne の Android アプリの開発環境について 2024</title>
    <link rel="alternate" href="https://tech.pepabo.com/2024/02/22/android-develop-environment-2024/"/>
    <id>https://tech.pepabo.com/2024/02/22/android-develop-environment-2024/</id>
    <published>2024-02-21T15:00:00+00:00</published>
    <updated>2024-02-24T00:04:00+00:00</updated>
    <author>
      <name>kyu</name>
    </author>
    <content type="html">&lt;p&gt;minne 事業部でモバイルアプリエンジニアをしている @kyu です。&lt;/p&gt;

&lt;p&gt;2024年も1ヶ月半が経ち、心機一転新たな気持ちで頑張っていきたいところですが、2023年度は最近のminne Androidの開発環境について紹介できていなかったので、紹介しようと思います。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tech.pepabo.com/2022/12/02/minne-android-tech-stack-2022/"&gt;2022年の記事はこちら&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="開発体制"&gt;開発体制&lt;/h2&gt;

&lt;p&gt;現在、minne Androidはエンジニア2名が基本体制ですが、加えてiOSエンジニアでありながらAndroid開発もできるパートナーがいるため、状況に応じて3名体制で作業を進めています。&lt;/p&gt;

&lt;h2 id="開発言語"&gt;開発言語&lt;/h2&gt;

&lt;p&gt;主にKotlinを使用して開発を行っています。minneは10年の歴史を持つアプリなので、一部のコードはJavaで記述されていますが、必要がない限りJavaの実装は避けるようにしています。2024年2月時点での言語比率は以下の通りです。
前回の報告と比較してKotlinの比率が増加しており、Javaの比率を7.2%減少させました。今年も積極的にKotlinへの移行を進め、来年にはJavaの比率を10%以下にできるよう頑張ります。&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/22/android-develop-environment-2024/language.png" alt="言語比率" /&gt;&lt;/p&gt;

&lt;h2 id="sdkversion"&gt;SdkVersion&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;minSdkVersion&lt;/td&gt;
      &lt;td&gt;24&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;targetSdkVersion&lt;/td&gt;
      &lt;td&gt;34&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;compileSdkVersion&lt;/td&gt;
      &lt;td&gt;34&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;compileSdkVersionは常に最新のAPIレベルに追従し、最新のAPIレベルのコンポーネントに触れる必要が生じた場合、素早く対応できるようにしています。
minSdkVersion を 25 に引き上げると前回の記事で記載していたのですが、minne Androidでは&lt;a href="https://developer.android.com/guide/playcore/feature-delivery"&gt;DynamicFeatureModule&lt;/a&gt; (DFM) のバグのため、minSdkVersionを更新することが難しい状況です。
ただし、現在 DFM の使用を廃止しようとしており、今年中に minSdkVersion を更新する予定です。&lt;/p&gt;

&lt;h2 id="フレームワークライブラリ"&gt;フレームワーク・ライブラリ&lt;/h2&gt;

&lt;h3 id="jetpack-compose"&gt;&lt;a href="https://developer.android.com/jetpack/compose?hl=ja"&gt;Jetpack Compose&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;前回の記事から引き続き Jetpack Compose を使用しています。
minne Android 内でも知見が溜まってきており、新規追加画面では基本的には Jetpack Compose で実装を進めるようにしています。また、既存画面も順次 Jetpack Compose に移行しています。2023年内において、いくつかの画面を Jetpack Compose で書き換えたため、その成果について別の機会でご紹介させていただきます。&lt;/p&gt;

&lt;h3 id="kotlin-coroutines"&gt;&lt;a href="https://kotlinlang.org/docs/coroutines-overview.html"&gt;Kotlin Coroutines&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Kotlin内での非同期処理にはCoroutinesを利用しています。Coroutinesはシンプルであり、Jetpackライブラリとの親和性も高いため、機能実装のコストが低くなります。JavaコードではRxJavaを使用しています。&lt;/p&gt;

&lt;h3 id="dagger-hilt"&gt;&lt;a href="https://developer.android.com/training/dependency-injection/hilt-android?hl=ja"&gt;Dagger Hilt&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;前回から引き続きDIライブラリには Hilt を使用しています。
Hilt のおかげで、簡単に依存性の注入を扱うことができるようになりました。モジュール間でのDIでHiltがよしなに行ってくれるので、マルチモジュール化に重宝しています。&lt;/p&gt;

&lt;h3 id="httpリクエスト"&gt;HTTPリクエスト&lt;/h3&gt;

&lt;p&gt;minneのWeb API は今後 GraphQL で実装する予定です。そのため、Android で GraphQL のHTTPリクエストを実現するために Apollo Kotlin を利用しています。既存のREST API では Retrofit2 を使用しています。&lt;/p&gt;

&lt;p&gt;画像表示のリクエストにおいて　Jetpack Compose では Coil を使用し、それ以外では Glide を使用しています。&lt;/p&gt;

&lt;h3 id="paging-3"&gt;&lt;a href="https://developer.android.com/topic/libraries/architecture/paging/v3-overview?hl=ja"&gt;Paging 3&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;ページネーションの実現にはPaging 3を導入しています。詳細な実装方法については&lt;a href="https://tech.pepabo.com/2021/10/18/android-paging3/"&gt;こちらのテックブログ&lt;/a&gt;を参照してください。&lt;/p&gt;

&lt;h3 id="mockito"&gt;&lt;a href="https://github.com/mockito/mockito"&gt;Mockito&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;モックライブラリとして Mockito を使用しています。ユニットテストで使用するオブジェクトのモック化が容易で、モック化されたオブジェクトの振る舞いも簡単に記述できるので、テストが簡潔に記述できます。&lt;/p&gt;

&lt;h2 id="アーキテクチャ"&gt;アーキテクチャ&lt;/h2&gt;

&lt;p&gt;アーキテクチャにおいては前回の記事から引き続き、Jetpackライブラリの親和性が高いため、AACのViewModelを採用し、MVVMアーキテクチャを採用しています。
また、マルチモジュールの採用を積極的に進めていますが、minneは大規模なアプリであるため、実装には時間がかかっています。&lt;/p&gt;

&lt;h2 id="cicd"&gt;CI/CD&lt;/h2&gt;

&lt;h3 id="github-actions"&gt;GitHub Actions&lt;/h3&gt;

&lt;p&gt;GitHub Enterprise Serverを使用しており、追加料金なしでGitHub Actionsを利用してCIを実行しています。&lt;/p&gt;

&lt;h3 id="fastlane-danger"&gt;fastlane、 Danger&lt;/h3&gt;

&lt;p&gt;Rubyが動作すればプラットフォームに依存せずCIを実行できるため、基本的にfastlaneを使用しています。また、PRのコードチェックにはDangerを利用しています。&lt;/p&gt;

&lt;h3 id="deploygate"&gt;DeployGate&lt;/h3&gt;

&lt;p&gt;PRやリリースの検証時にアプリをテスターに配布するために DeployGate を使用しています。個人のサイドローディングが許可されていないため、Firebase App Distribution の代替手段として DeployGate を利用しています。&lt;/p&gt;

&lt;h3 id="dependabot"&gt;Dependabot&lt;/h3&gt;

&lt;p&gt;minneでは Version Catalog を利用してライブラリを管理しており、その管理されているライブラリのアップデートを検知して自動で Pull Request を生成するために Dependabot を使用しています。
以前の記事では Renovate を使用していましたが、ペパボでは Dependabot を推奨しているのと、 Dependabot が Version Catalog に新たに対応したため、現在は Dependabot に完全移行しました。&lt;/p&gt;

&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;

&lt;p&gt;2024年2月時点の minne の Android アプリ開発の現状を紹介しました。
前回記事からの差分は技術の変化はあまりありませんが、引き続き Jetpack Compose を中心に新しい技術を取り入れ、レガシーコードのリファクタリングやユーザー体験の向上に努めています。&lt;/p&gt;

&lt;p&gt;私たちの目標は、常にユーザーにとって価値のあるアプリを提供し続けることです。そのために、新しい技術やベストプラクティスを積極的に取り入れ、チームと共に成長し続けています。&lt;/p&gt;

&lt;p&gt;minne の Android アプリ開発に興味がある方、一緒にスキルアップしていきたい方はぜひご応募ください！私たちと一緒に成長し、素晴らしいアプリを作りましょう！&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>日本CTO協会による合同ISUCON研修の紹介</title>
    <link rel="alternate" href="https://tech.pepabo.com/2024/02/16/isucon-2023/"/>
    <id>https://tech.pepabo.com/2024/02/16/isucon-2023/</id>
    <published>2024-02-15T15:00:00+00:00</published>
    <updated>2024-02-24T00:04:00+00:00</updated>
    <author>
      <name>harukin</name>
    </author>
    <content type="html">&lt;p&gt;こんにちは！技術部プラットフォームグループの&lt;a href="https://twitter.com/harukin721"&gt;harukin&lt;/a&gt;, &lt;a href="https://tech.pepabo.com/authors/pochy/"&gt;pochy&lt;/a&gt;です。&lt;/p&gt;

&lt;p&gt;この記事では、「&lt;a href="https://isucon.net/"&gt;ISUCON&lt;/a&gt;」を模したパフォーマンスチューニング研修を複数社合同で実施した概要と、そのための準備について紹介します。&lt;/p&gt;

&lt;h2 id="研修について"&gt;研修について&lt;/h2&gt;

&lt;h3 id="目的"&gt;目的&lt;/h3&gt;

&lt;p&gt;今回の研修の目的は次のものとしました。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;パフォーマンスチューニングの問題を会社横断でチームを組成し取り組むことで、サーバサイドやインフラのパフォーマンス・チューニングを中心に幅広い知識を総動員して課題解決に望む。&lt;/li&gt;
  &lt;li&gt;課題解決過程のコミュニケーションを通じて、会社の枠を超えた同期作りを促進する。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="概要"&gt;概要&lt;/h3&gt;

&lt;p&gt;今回の研修では、チームごとにパフォーマンスチューニングの課題に挑戦しました。&lt;/p&gt;

&lt;p&gt;実際のISUCONのように、各チームにwebサーバーを貸し出す形式です。各チームはそのアプリケーションを時間内にパフォーマンスチューニングし、最適化された度合いによってチームに点数をつけます。時間が終了した時点で最も点数が高かったチームが優勝となります。&lt;/p&gt;

&lt;p&gt;なお、ISUCONとは、次のようなイベントです。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;ISUCONとはLINEヤフー株式会社が運営窓口となって開催している、お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトルです。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://isucon.net/archives/58076588.html"&gt;ISUCON13 イベントレポート : ISUCON公式Blog&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="参加者運営者"&gt;参加者、運営者&lt;/h3&gt;

&lt;p&gt;この研修は&lt;a href="https://cto-a.org/"&gt;一般社団法人日本CTO協会&lt;/a&gt;（以下、日本CTO協会）が「CTO協会 合同ISUCON研修」（以下、合同ISUCON研修）として主催しました。GMOペパボを含めた複数社から、研修の運営者と参加者が集まりました。&lt;/p&gt;

&lt;p&gt;日本CTO協会に所属する企業の、2021〜2023年新卒入社のエンジニアを主な対象参加者としました。
東京・大阪・福岡のそれぞれで会場が設営され、各会場はビデオ通話でやり取りをしました。
それぞれの会場はPingCAP株式会社様、株式会社YOLO JAPAN様、Fukuoka Growth Next様に提供いただきました。ありがとうございます！&lt;/p&gt;

&lt;p&gt;運営は、日本CTO協会に所属するCTOたちと、この記事を執筆している私たちを含めた各社の若手エンジニア数名で行いました。
ペパボの中では、日本CTO協会で活動している&lt;a href="https://twitter.com/kurotaky"&gt;kurotaky&lt;/a&gt;から募集がされました。&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/16/isucon-2023/slack_wakate.png" alt="社内のSlackで「だれか〜」「若手〜」と募っている。pochy, harukinがそれぞれ応えている。「これ着ていきましょう」という提案とともに&amp;quot;ワタシハAWSチョットデキル&amp;quot;と書かれたTシャツを販売するsuzuriのリンクが貼られている。" title="社内のSlackで運営者の募集があった" /&gt;&lt;/p&gt;

&lt;p&gt;運営のために集まった若手エンジニアたちは、競技用サーバーの立ち上げと配布の仕組み、ベンチマーカー、スコアを表示するためのポータルを構築しました。また、当日は会場に行き、参加者のアシストや壊れたベンチマーカーの修正をおこなっていました。参加者がパフォーマンスチューニングに取り組んでいる裏で、運営者もリアルタイムで障害対応をしていたのですね。&lt;/p&gt;

&lt;h2 id="研修システムのアーキテクチャ"&gt;研修システムのアーキテクチャ&lt;/h2&gt;

&lt;p&gt;私たちが構築したシステムのアーキテクチャを説明します。&lt;/p&gt;

&lt;p&gt;私たち若手エンジニアたちは、日常業務の合間や少しの空いた時間を利用して、合同ISUCON研修の開催に向けて必要なシステムの準備を行いました。&lt;/p&gt;

&lt;p&gt;GMOペパボの2人は、主にベンチマーカーとポータルの準備に携わりました。&lt;/p&gt;

&lt;h3 id="解説"&gt;解説&lt;/h3&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/16/isucon-2023/eye-catch.png" alt="構成図の画像" /&gt;&lt;/p&gt;

&lt;p&gt;システムのインフラとしてはAWSを利用しました。なお、この研修のインフラを構築するために、AWS様にクーポン付与の形式で料金を負担していただきました。ありがとうございます！&lt;/p&gt;

&lt;p&gt;このシステムは、大まかに次の3つの要素で構成されています。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;競技用サーバー
    &lt;ul&gt;
      &lt;li&gt;それぞれのチームがパフォーマンスチューニングするwebサーバー&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ベンチマーカー
    &lt;ul&gt;
      &lt;li&gt;競技用サーバーに負荷をかけて点数を算出するコンポーネント&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;ポータル
    &lt;ul&gt;
      &lt;li&gt;参加者が、各チームの点数・順位・ベンチマーカーからのログを閲覧するためのwebアプリケーション&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;それぞれ詳細に解説します。&lt;/p&gt;

&lt;h4 id="競技用サーバー"&gt;競技用サーバー&lt;/h4&gt;

&lt;p&gt;今回の研修では、&lt;a href="https://twitter.com/catatsuy"&gt;catatsuy&lt;/a&gt;さんの&lt;a href="https://github.com/catatsuy/private-isu"&gt;private-isu&lt;/a&gt;を使用させていただきました。private-isuを動かせる環境の準備には、&lt;a href="https://twitter.com/matsuu"&gt;matsuu&lt;/a&gt;さんの&lt;a href="https://github.com/matsuu/cloud-init-isucon"&gt;cloud-init-isucon&lt;/a&gt;を利用させていただきました。AMI化したものを当日の参加者に配布しました。この準備作業は、GMOペパボではない若手エンジニアの方に行っていただきました。&lt;/p&gt;

&lt;h4 id="ベンチマーカー"&gt;ベンチマーカー&lt;/h4&gt;

&lt;p&gt;ベンチマーカーは、ECS Fargate、Lambda、SQSを使用して構築しました。&lt;/p&gt;

&lt;p&gt;私はTerraformを使用してAWSインフラを構築する作業を担当しました。Lambdaのファイルディスクリプタの制限により、複数のチームが同時にベンチマーカーを実行してもベンチマーカーが停止しないように、ECSタスク、Lambda、SQSを連携させる構成にしました。競技後半には多数のチームが一斉にベンチマーカーを実行することが予想されていたので、その対応として運営チームは裏でECSタスク数を増やすなどの調整を行いました。&lt;/p&gt;

&lt;p&gt;競技用サーバーからのベンチマーカー起動と、結果をポータル画面で表示する一連のプロセスは、GMOペパボではない若手エンジニアの方々に実装を行っていただきました。競技用サーバーからのリクエストの際にIPアドレスを取得し、これをSQSへメッセージとしてキューに入れることから始まり、Lambdaがその情報に基づいてECSタスクを起動し、ECSタスクがSQSからメッセージを取得してベンチマーカーを実行し、その結果をポータル画面のスコアに表示するまでの一連のプロセスをGoで実装されており、とても学びになりました。&lt;/p&gt;

&lt;h4 id="ポータル"&gt;ポータル&lt;/h4&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/16/isucon-2023/portal.jpg" alt="当日のポータル画面の画像" /&gt;
※不思議なチーム名がたくさんありますが、これらは当日に各チームが自ら決めたものです。&lt;/p&gt;

&lt;p&gt;ポータルは、pochyが構築しました。&lt;/p&gt;

&lt;p&gt;AWSのAmplifyを利用することでポータルをwebアプリケーションとして構築・デプロイしました。データストアには、NoSQLのDynamo DBを利用することで、各チームのポイントを時刻と紐づけて保持しました。APIエンドポイントにはGraphQLでデータをやりとりするためのAppSyncを採用し、ポイントの取得と更新を実現しました。取得はポータル画面を利用するブラウザから、更新はベンチマーカーから叩かれるので、更新の際にはAPIキーで認可をかけるように実装しました。クライアントアプリはVue.jsとCanvas APIを用いて実装し、各チームの得点推移と現在の順位表を表示するようにしました。チームの認証には、web用のユーザー管理・認証を行うCognitoを利用しました。&lt;/p&gt;

&lt;h2 id="感想"&gt;感想&lt;/h2&gt;

&lt;h3 id="harukin"&gt;harukin&lt;/h3&gt;

&lt;p&gt;合同ISUCON研修を通じて、事前準備や研修後の懇親会で他社の同年代のエンジニアの方々と交流する機会を得られて、とても貴重な経験になりました。事前準備の際には若手エンジニアの皆さんの技術力の高さに刺激を受け、自分ももっとできることを増やしていきたいと思いました。&lt;/p&gt;

&lt;p&gt;ベンチマーカーの担当をし、Terraformを用いてECS Fargateを扱うことは初めてだったのでとても学びがありました。また、若手エンジニアの皆さんからECS、Lambda、SQSの連携プロセスについて学ぶことができ、視野を広げることができました。Terraformで完全にIaC化できなかった部分もありますが、これを今後の反省として受け止めて、自己研鑽を積み重ねていきたいと思います。当日のベンチマークはいくつかの小さな問題が発生しましたが、機能停止や競技中断といった深刻な事態には至らずに無事に終えることができたのでほっとしました。競技終了後の&lt;code&gt;terraform destroy&lt;/code&gt;が気持ちよかったです。&lt;/p&gt;

&lt;h4 id="pochy"&gt;pochy&lt;/h4&gt;

&lt;p&gt;まずよかった点としては、他社の若手エンジニアと交流を持てたことです。日常の仕事ではほとんど社内のことしか見られませんが、このイベントでは複数の会社から集まったエンジニアたちと協力してシステムを作れました。GitHubやDiscordで定期的にやりとりをしつつ、当日までに構築を間に合わせました。このような体験は日々の業務ではできないため、私にとっては新鮮でした。
研修終了後の懇親会でも、たくさんの人とおしゃべりができました。会社の枠を飛び越え、Web業界のエンジニア仲間として歓談できる機会は貴重なため、参加してよかったと感じました。&lt;/p&gt;

&lt;p&gt;反省点としては、最後の最後でポータル画面が一部壊れてしまった点です。ポータルの構築は私が担当していたのですが、データ量を考慮していなかったため、データが増えた終盤では一つの画面ですべての点数を表示できなくなってしまいました。パフォーマンスチューニングの研修のためのシステムなのに、可用性を高く保てなかった経験は自分の心に深く刺さっています。このブログを半年以上も執筆できなかった理由の一つが、この恥ずかしさだったのかもしれません…。たぶん一生忘れないので糧にしたいです。「流した冷や汗は嘘をつかない」は真だなと感じました。&lt;/p&gt;

&lt;h2 id="おわり"&gt;おわり&lt;/h2&gt;

&lt;p&gt;以上、日本CTO協会による複数社合同研修の紹介でした。今年も合同研修の企画が進んでいるようなので、またどこかでお会いしましょう。&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>新人インフラエンジニアが研修を受けてみた</title>
    <link rel="alternate" href="https://tech.pepabo.com/2024/02/14/infra-kensyu/"/>
    <id>https://tech.pepabo.com/2024/02/14/infra-kensyu/</id>
    <published>2024-02-13T15:00:00+00:00</published>
    <updated>2024-02-24T00:04:00+00:00</updated>
    <author>
      <name>pab0tesu</name>
    </author>
    <content type="html">&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;2023年12月13〜19日の間、ペパボ技術部の新人２名が、&lt;a href="https://heartbeats.jp/"&gt;株式会社ハートビーツ&lt;/a&gt;様ご監修のもとに実施されたインフラ領域に関する研修に参加しました。&lt;/p&gt;

&lt;h2 id="目的"&gt;目的&lt;/h2&gt;
&lt;p&gt;本研修を通じて、多くのサービスで利用されるインフラ基盤の汎用的な技術の習得を目指し、今後、業務にジョインする際にスムーズに各ツールや技術を扱えるようにする。
また、インフラ技術に対する距離感を縮め、自信を持って業務に臨めるようにしたい。&lt;/p&gt;

&lt;h2 id="研修でやったこと"&gt;研修でやったこと&lt;/h2&gt;

&lt;p&gt;以下が最終的な構成内容となります。&lt;/p&gt;

&lt;p&gt;具体的にはAWSに構築したパブリックなVPC上で２台のEC2の冗長構成をとり、
ロードバランサとしてはALBでHTTP/HTTPSを受けるように構えております。
なお、ドメインの名前解決にはRoute53を活用しています。&lt;/p&gt;

&lt;p&gt;サービス運用中のログ取得にはfluentdを利用し、取得した内容はS3に取集しております。
ログの確認はAmazon Athenaを利用します。&lt;/p&gt;

&lt;p&gt;また、今回の構成ではAWSのリソースはTerraformで定義し、サーバの構築にはAnsibleを利用しています。&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/14/infra-kensyu/images/saisyukousei.png" alt="構築例" /&gt;&lt;/p&gt;

&lt;h3 id="1日目aws上でインフラ基盤を構築する"&gt;1日目：AWS上でインフラ基盤を構築する&lt;/h3&gt;

&lt;h4 id="内容awsでマネージメントコンソールからインフラ基盤を手作業で構築する"&gt;内容：AWSでマネージメントコンソールからインフラ基盤を手作業で構築する&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;pab0tesu &amp;amp; chiroru&lt;/strong&gt;：AWSについては機能のアップデート・追加のみならず、マネジメントコンソールのUIの変化も非常に多く、
技術として知っているものでも以前試したときから操作感が異なっており、作業の際に都度確認する必要がありました。&lt;/p&gt;

&lt;p&gt;また、この作業を実際の業務に置き換えた場合、担当者が慣れ親しんだUIも時代や世代により異なり、
手作業で変更した際に、担当者間の認識のズレが起因してヒューマンミスの誘発や冪等性の担保が難しくなるのではと疑問が残りました。&lt;/p&gt;

&lt;p&gt;講師の方からも通常業務で手作業で操作するよりも、コードとして構成管理するのが一般的になりつつあると説明いただきました。&lt;/p&gt;

&lt;p&gt;敢えてマネジメントコンソールから作業をしてみることで、2日目以降より進めたIaCの効果を前もって感じられるセクションとなりました。&lt;/p&gt;

&lt;h3 id="2日目インフラ基盤をterraformを用いて構築する"&gt;2日目：インフラ基盤をTerraformを用いて構築する&lt;/h3&gt;

&lt;h4 id="内容インフラ基盤をterraformで構築しgithub-actionsを利用してterraform-planの実行を自動化する"&gt;内容：インフラ基盤をTerraformで構築し、GitHub Actionsを利用して&lt;code&gt;terraform plan&lt;/code&gt;の実行を自動化する&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;pab0tesu &amp;amp; chiroru&lt;/strong&gt;：Terraformを用いたAWSリソースの展開については、先の手作業での操作に比べてもヒューマンミスの減少や冪等性の担保といった点で、非常に有効な技術であり、その効果を改めて実感できる機会となりました。&lt;/p&gt;

&lt;p&gt;前日マネジメントコンソールで作成したリソースを複数の機能として分解し、コードに起こした際に組み立てていくような感覚からも、各リソースの理解をより深めることができました。&lt;/p&gt;

&lt;p&gt;また、Terraformのdry-runである&lt;code&gt;terraform plan&lt;/code&gt;をGitHub Actionsで自動化し、pushと同時にTerraformにて展開される各リソースの状態を一覧できる点は、複数人でインフラ基盤を構築する際のレビューで実行結果等を確認しやすくなるというメリットを実感することができました。&lt;/p&gt;

&lt;h3 id="3日目ansibleを利用してwebサーバを実装する"&gt;3日目：Ansibleを利用してwebサーバを実装する&lt;/h3&gt;

&lt;h4 id="内容awsリソースに対してansibleを用いてサーバ構築を自動化しgithub-actionsを利用してplaybookのチェックを自動化する"&gt;内容：AWSリソースに対してAnsibleを用いてサーバ構築を自動化し、GitHub Actionsを利用してPlaybookのチェックを自動化する&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;pab0tesu&lt;/strong&gt;：Ansibleについては普段の業務でも利用する機会は多く、社内でも様々なサービスで利用されている技術ですが、実際に利用する際には各モジュールの機能についてあまり深く調査して利用するといった経験がありませんでした。そのためこれまではリファレンスを参照しとりあえず利用するという方法で書いていました。今回の研修では各モジュールをできる限り深掘りし、「この機能ならより簡単に実装できそう」、「この設定を実現したい場合、実はこっちのモジュールの方が良さそう」という観点を持って実装に臨むことができました。&lt;/p&gt;

&lt;p&gt;今後もAnsibleに限らず、技術とむきあう際には必ず「深掘り」というフェーズを加えて、技術に対する理解度を上げてから取り組むようにいたします！&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;chiroru&lt;/strong&gt;：Ansibleとは何か？やドキュメントの見方、手元で動作確認を行う方法やファイル読み込みの順序、リポジトリ構成の仕組み、moduleや変数の決まり事など、手を動かしながらAnsibleの基礎的な仕組みを学ぶことができました。
普段見ているサービスの中でも、Ansibleで管理されたコードを読むことがあったり、Puppetからの移行タスクなどもあるので、実務の中でもすぐに使える知識を習得できました。&lt;/p&gt;

&lt;h3 id="4日目構築されたサーバ上でdockerによるアプリケーションの展開"&gt;4日目：構築されたサーバ上でDockerによるアプリケーションの展開&lt;/h3&gt;

&lt;h4 id="内容サンプルアプリケーションのコンテナイメージをdocker上で実行し操作について学んだ"&gt;内容：サンプルアプリケーションのコンテナイメージをDocker上で実行し、操作について学んだ&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;pob0tesu&lt;/strong&gt;：Dockerという技術の特性上さまざまなアーキテクチャ上でも同一の環境を準備でき、それらの共有が容易に行えるという点で非常に大きなメリットを感じることができました。
具体的には、エディターであるVisual Studio Codeの機能ではありますが、Dockerと組み合わせたdevcontainerを利用すれば事前に準備した共通の環境をスピーディにチーム間で共有することが可能となり、新たなメンバーがジョインされる際にもつまずきを少なくすることができるのではと思っております。
こちらも是非積極的に利用していこうと思います！&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;chiroru&lt;/strong&gt;：以前よりDockerを利用した開発経験はありましたが、実際に自分でコンテナを定義するのではなく、用意されたイメージを利用する機会が多かったです。
今回自分でコンテナを定義する際にデータを永続化する必要があり、そこでボリュームを作成したのですが、普段業務で利用するK8sのボリュームという機能に繋がり、コンテナ技術における「ボリューム」の役割についての理解が深まりました。&lt;/p&gt;

&lt;h3 id="5日目構築したサーバのログ出力からさまざまな方法で調査解析を行う"&gt;5日目：構築したサーバのログ出力からさまざまな方法で調査・解析を行う&lt;/h3&gt;

&lt;h4 id="内容構築したサーバが出力するログをlinuxコマンドを用いて確認し解析を行う--aws上でもamazon-athenaを利用して同様のログ出力を抽出する"&gt;内容：構築したサーバが出力するログをLinuxコマンドを用いて確認し解析を行う / AWS上でもAmazon Athenaを利用して同様のログ出力を抽出する&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;pob0tesu&lt;/strong&gt;：Linuxサーバから出力されるログの確認については最初はうまく取得できない項目などがありましたが、講師の皆様にフォローいただき、最終的には必要な情報を汲み取ることができるようになりました。
しかしまだまだshell力不足を実感する良い機会となりました、、、今後も精進いたします！
また、Amazon Athenaでのログ抽出は初めての経験でしたが、SQLの非常に柔軟な絞り込みが可能なこともあり、出力結果も見やすく整備されていたため、あまり迷いなく利用することができました。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;chiroru&lt;/strong&gt;：現在業務では、調査に必要な情報を取得する際に、どのツールで何ができ、どこを見ればよいのかを勉強中ですが、必要な情報をコマンドでも取得できるようにすることも重要だと感じました。
ツールのログ解析との比較として、awkなどを利用したログ解析も行ったのですが、ツールに比べ欲しい情報をコンパクトに取得できる(小回りが効く)イメージを持ちました。そこで、情報を取得する手段としてCUIという選択肢もあると、より欲しい情報を最短で手に入れられるようになるのではないか？と感じました。
今後は手元で取得する方法も学びつつ、欲しい情報を状況に応じて取得できる手段を増やしていきたいです。&lt;/p&gt;

&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;pab0tesu&lt;/strong&gt;：今回の研修では主に、パブリッククラウド上でIaCを用いたリソースの展開、サーバの構築、またその先のログ取得等の運用に関わる部分までを経験することができました。&lt;/p&gt;

&lt;p&gt;前述の通り各セクションごとに大きな収穫があったことはもちろん、今後の技術に向き合う際の心構えをアップデートできました。
また研修の合間などで、最新の技術動向や講師の皆様がキャッチアップされている情報等を共有いただいたことも、大変ためになる内容でした。&lt;/p&gt;

&lt;p&gt;研修で得られた技術やマインドセットは早速業務でも活躍できるものと思います。
本研修の機会をご用意・ご手配いただいた皆様、本当にありがとうございました！&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;chiroru&lt;/strong&gt;：研修を通じて技術的な知識を得ただけでなく、便利な技術やツールを活用する際の、その背後にある概念や仕組みを理解する重要性について、改めて考えるようになりました。&lt;/p&gt;

&lt;p&gt;それは例えば、必要な情報を可視化してくれているツール(Amazon Athena)と同じものを取得するコマンドを考えたり、講師の方との対話の中で、K8sで使われるコンテナの設定がLinuxのcgroupsという概念に基づいていることを初めて知る中で感じました。&lt;/p&gt;

&lt;p&gt;現在の業務の中では、K8sに触れる機会が多いため「K8sで実現しているこの機能の元はどんな仕組みなのか？」と、シンプル-&amp;gt;複雑な仕組みの理解 へのアプローチがしやすくなっているのではないかと思います。&lt;/p&gt;

&lt;p&gt;便利なものに触れることで、その背後に潜む仕組みや原理をのぞくことを心がけ、利便性と裏側の仕組みの双方を大切にしていきたいと思いました。&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>PHP8系へのバージョンアップに伴うライブラリのリプレイス その1 〜AspectMock編〜</title>
    <link rel="alternate" href="https://tech.pepabo.com/2024/02/09/aspectmock-to-mockery/"/>
    <id>https://tech.pepabo.com/2024/02/09/aspectmock-to-mockery/</id>
    <published>2024-02-08T15:00:00+00:00</published>
    <updated>2024-02-24T00:04:00+00:00</updated>
    <author>
      <name>matsusuke</name>
    </author>
    <content type="html">&lt;p&gt;こんにちは。@matsusukeと申します。ホスティング事業部でWebアプリケーションエンジニアとしてプロダクト開発・運用保守業務を行っています。
現在ホスティング事業部ではアプリケーションのPHPバージョンを8系に上げるプロジェクトを進めています。
8系へのバージョンアップに伴い、いくつかの依存ライブラリが動作しない事例があったので、今回の記事を皮切りに複数回に渡って紹介していきたいと思います。&lt;/p&gt;

&lt;p&gt;第一回目である今回は、PHPのユニットテストのテストダブルライブラリであるAspectMockの事例について紹介します。&lt;/p&gt;

&lt;h2 id="aspectmockについて"&gt;AspectMockについて&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/Codeception/AspectMock"&gt;AspectMock&lt;/a&gt;はPHPUnitでテストダブルを作成できるライブラリです。
AspectMockは、PHPUnitで用いるテストダブルをほぼ無制限に置き換え可能という特徴を持っています。
具体的には、下記のようなケースでもテストダブルを作成することが可能です。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;アプリケーションコード内のstaticなメソッド&lt;/li&gt;
  &lt;li&gt;メソッド内で依存クラスのインスタンス化をしている場合&lt;/li&gt;
  &lt;li&gt;テスト対象メソッド内で自クラスのメソッドを呼んでいる場合&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;このような特徴を持っているため、staticメソッドをアプリケーション内で多く使っていたり、メソッド内でインスタンスを作成しているような密結合なコードでもユニットテストを作成でき、
非常に便利なライブラリです。&lt;/p&gt;

&lt;p&gt;ホスティング事業部のアプリケーションは歴史が古く、作成された当初はテストコードが作られていない実装が多くありました。
そのような状態から、できるだけリファクタリング工数をかけずにテストコードを導入する際にマッチしたライブラリと言えます。&lt;/p&gt;

&lt;p&gt;このような背景がある中で使われてきたAspectMockですが、2024年2月時点で、PHP8系への対応は行われていません。（Issueは&lt;a href="https://github.com/Codeception/AspectMock/issues/202"&gt;こちら&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;PHP8環境でPHPUnitを動かすには、AspectMockを使って作成されたテストダブルを別の方法で作成するしかありません。
そこで、代替となるツールとしてPHP8系に対応している、かつ、スター数が多い&lt;a href="https://github.com/mockery/mockery"&gt;Mockery&lt;/a&gt;を使うことにしました。&lt;/p&gt;

&lt;h2 id="リプレイスを行う"&gt;リプレイスを行う&lt;/h2&gt;
&lt;p&gt;上述したように、テストダブルへの置き換えの制約が少ないAspectMockと比較し、Mockeryではアプリケーションコードによってはそのまま置き換えが行えないケースもあります。
可能な限り実装・テストコードの振る舞いを変更せずにMockeryへのリプレイスを行った事例についていくつか紹介します。&lt;/p&gt;

&lt;h3 id="テスト対象メソッド内で自クラスのメソッドを呼んでいるケース"&gt;テスト対象メソッド内で自クラスのメソッドを呼んでいるケース&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;serviceAvailable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// true or falseを返す何らかの処理&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;serviceAvailable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// true or falseを返す何らかの処理&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上記のようなアプリケーションコードを想定します。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;AspectMock\Test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_true&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hoge'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hoge'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'serviceAvailable'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;AspectMockを用いた場合、テストダブルの作成は&lt;code&gt;AspectMock::double&lt;/code&gt;によって行えていました。
Mockeryの場合、通常であれば&lt;code&gt;Mockery::mock(Hoge::class)&lt;/code&gt;によってテストダブルの作成が行えます。
しかし、今回のようにテスト対象メソッド内で自クラスのメソッドを呼んでいるケースではテストダブルによる置き換えがうまくいきません。&lt;/p&gt;

&lt;p&gt;そこで、Mockeryの&lt;code&gt;パーシャルモック&lt;/code&gt;機能を用います。&lt;/p&gt;

&lt;p&gt;パーシャルモックとはモックしたいメソッドの挙動のみを変更できるモックオブジェクトのことを指します。
下記のように&lt;code&gt;makePartial&lt;/code&gt;メソッドを用いることで、パーシャルモックを作成します。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Mockery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_true&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;makePartial&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serviceAvailable'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;これにより、&lt;code&gt;Hoge&lt;/code&gt;クラスの&lt;code&gt;settingEnabled&lt;/code&gt;メソッドと&lt;code&gt;serviceAvailable&lt;/code&gt;メソッドの挙動だけモックすることができます。
Mockeryのパーシャルモックについての詳しい説明は&lt;a href="https://docs.mockery.io/en/latest/reference/partial_mocks.html"&gt;こちら&lt;/a&gt;をご覧ください。&lt;/p&gt;

&lt;h4 id="モックしたメソッドのアクセス権"&gt;モックしたメソッドのアクセス権&lt;/h4&gt;

&lt;p&gt;Mockeryへリプレイスしたテストについて、上記のままだとモックした&lt;code&gt;settingEnabled&lt;/code&gt;メソッドと&lt;code&gt;serviceAvailable&lt;/code&gt;メソッドへのアクセス権が&lt;code&gt;private&lt;/code&gt;であるため、テストに失敗します。
AspectMockによってモックした場合と挙動が変わるポイントになります。
Mockeryで作成したモックのメソッドは&lt;code&gt;private&lt;/code&gt;のままだとテストコードでの実行は難しいですが、&lt;code&gt;protected&lt;/code&gt;メソッドの場合アクセス可能にできる方法があります。&lt;/p&gt;

&lt;p&gt;それは、&lt;code&gt;shouldAllowMockingProtectedMethods&lt;/code&gt;メソッドを用いることです。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;serviceAvailable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// true or falseを返す何らかの処理&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;serviceAvailable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// true or falseを返す何らかの処理&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Mockery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_true&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;makePartial&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldAllowMockingProtectedMethods&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serviceAvailable'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上記のように、&lt;code&gt;protected&lt;/code&gt;メソッドでもアクセスできるようになる根拠はMockeryの&lt;a href="https://github.com/mockery/mockery/blob/1.6.x/library/Mockery/Mock.php#L219-L224"&gt;ソースコード&lt;/a&gt;を読み解くことで確認できます。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isPrivate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="s2"&gt;() cannot be mocked as it is a private method"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$allowMockingProtectedMethods&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$rm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isProtected&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="s2"&gt;() cannot be mocked as it is a protected method and mocking protected methods is not enabled for the currently used mock object. Use shouldAllowMockingProtectedMethods() to enable mocking of protected methods."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;確かに、&lt;code&gt;protected&lt;/code&gt;メソッドの場合のみ、&lt;code&gt;$allowMockingProtectedMethods&lt;/code&gt;がtrueであればアクセス権が付与されていることがわかりました。&lt;/p&gt;

&lt;p&gt;なお、アプリケーションコードのメソッドのアクセス権をprivateからprotectedへリファクタリングする際は、下記のように実装に影響がないことを確認する必要があります。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hogeクラスを継承する子クラスがないこと&lt;/li&gt;
  &lt;li&gt;Hogeクラスが他のクラスの子クラスとなっていないこと&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="メソッド内で依存クラスのインスタンス化をしているケース"&gt;メソッド内で依存クラスのインスタンス化をしているケース&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Fuga&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;serviceAvailable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;Class&lt;/span&gt; &lt;span class="nc"&gt;Fuga&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// true or falseを返す何らかの処理&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;serviceAvailable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// true or falseを返す何らかの処理&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;次に上記のような実装における&lt;code&gt;Hoge&lt;/code&gt;クラスの&lt;code&gt;available&lt;/code&gt;メソッドのテストケースを作成しようとする場合について想定します。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;available&lt;/code&gt;メソッドが&lt;code&gt;Fuga&lt;/code&gt;クラスに依存しているため、&lt;code&gt;Hoge&lt;/code&gt;クラスの実装を何も変更しなくても、&lt;code&gt;Fuga&lt;/code&gt;クラスの実装を変更したタイミングで Hogeクラスの単体テストが壊れてしまう可能性があります。
こういった密結合なコードでも、AspectMockであれば&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;AspectMock\Test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_true&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Fuga'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Fuga'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'serviceAvailable'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;のように、テスト対象メソッドでインスタンス化されているクラスのテストダブルも作成可能です。
しかし、Mockeryの場合はテストダブル作成時に一工夫する必要があります。&lt;/p&gt;

&lt;h4 id="オーバーロード機能を使う"&gt;オーバーロード機能を使う&lt;/h4&gt;

&lt;p&gt;Mockeryではモック生成時に既存のクラスをオーバーロードしてモックを作成できる機能があります。
これにより、テスト対象メソッドでインスタンス化されているような場合でも依存しているクラスのメソッドの振る舞いをモックできるようになります。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Mockery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_true&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overload:Fuga'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serviceAvailable'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;オーバーロード機能についての詳しい説明は&lt;a href="https://docs.mockery.io/en/latest/cookbook/mocking_hard_dependencies.html?highlight=overload#mocking-hard-dependencies-new-keyword"&gt;こちら&lt;/a&gt;をご覧ください。&lt;/p&gt;

&lt;p&gt;上記で述べたような「&lt;code&gt;private&lt;/code&gt;メソッドのテストをするために&lt;code&gt;protected&lt;/code&gt;メソッドへ変更する」ことや「実装メソッド内でインスタンスの生成を行っている」ことは、本来であればアンチパターンとして語られることがあるような事象です。&lt;/p&gt;

&lt;p&gt;しかし、今回のプロジェクトの目的である「PHPのバージョンを上げてユニットテストを全てパスさせる」ことを最優先に考えたため、リファクタリングを行うべき箇所についてあえて行わず、テストコードの修正によって回避したという背景があります。&lt;/p&gt;

&lt;h2 id="リプレイスを行う上での課題"&gt;リプレイスを行う上での課題&lt;/h2&gt;

&lt;p&gt;AspectMockからMockeryへのリプレイスがうまくいかなったケースもあるため、その事例も紹介します。&lt;/p&gt;

&lt;h3 id="静的メソッドのモックができない"&gt;静的メソッドのモックができない&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Fuga&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;settingEnabled&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 何らかの処理&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ロギングをする静的メソッド&lt;/span&gt;
            &lt;span class="nv"&gt;$logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Piyo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Disabled'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HogeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Throw Disabled'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;上記のような実装を想定します。
AspectMockを用いた場合のテストコードは下記のようになっていました。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;AspectMock\Test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Fuga'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nv"&gt;$loggerResMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;stdClass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nv"&gt;$piyoMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Piyo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'logger'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$loggerResMock&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="c1"&gt;// loggerが呼ばれ、例外が発生すること。&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$piyoMock&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verifyInvoked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'logger'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;静的メソッドのモックをMockeryを使って置き換えようとすると、下記のようなテストコードになります。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Mockery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overload:Fuga'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$loggerResMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;stdClass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nv"&gt;$piyoMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overload:Piyo'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// loggerが呼ばれ、例外が発生する。&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$piyoMock&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verifyInvoked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'logger'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$piyoMock&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'logger'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;しかし、上記のように静的メソッドを実行している&lt;code&gt;Piyo&lt;/code&gt;クラスをオーバーロードしようとした場合、下記のようなエラーが発生してしまいます。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;Mockery&lt;span class="se"&gt;\E&lt;/span&gt;xception&lt;span class="se"&gt;\R&lt;/span&gt;untimeException: Could not load mock Piyo, class already exists
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;このエラーは、PHPUnit実行時に静的メソッドを実行している&lt;code&gt;Piyo&lt;/code&gt;クラスが既にロードされてしまっており発生したエラーであると考えられます。&lt;/p&gt;

&lt;h4 id="どのように回避したか"&gt;どのように回避したか？&lt;/h4&gt;
&lt;p&gt;静的メソッドのモックを行えるようにするには、PHPUnit実行時のクラスのオートロードの設定を見直すなどの対応策が考えられますが、今回は「PHPをバージョンアップし、ユニットテストをパスさせる」という目的が大前提にあるので、リファクタリング工数をできるだけかけずに既存のテストを通過させる選択を取る必要がありました。&lt;/p&gt;

&lt;p&gt;そのような背景から、今回のようなケースのテストコードは下記のように修正しました。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Mockery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HogeTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_available_error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hoge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overload:Fuga'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fuga&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'settingEnabled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 静的メソッドが呼ばれることを確認するのではなく、例外がスローされることを期待する&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expectException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HogeException&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expectExceptionMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Throw Disabled'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hoge&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;available&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;今回のリプレイス対象のテストコードは静的メソッドのテストダブルを作れなくても振る舞いを変えずにテストコードの変更が行えました。
しかし、静的メソッドの戻り値によって単体テストの結果が変わるような密結合な実装の場合、今回のような回避策は使えないため、PHPUnit実行時のオートロードの部分を見直すなどの根本的な解決が必要になりそうです。&lt;/p&gt;

&lt;h2 id="最後に"&gt;最後に&lt;/h2&gt;
&lt;p&gt;今回のような、PHPのバージョンアップによる依存ライブラリのリプレイスは個人的に初めて経験した業務となりました。
ライブラリによってできることとできないことがあるため、「バージョンアップしたPHP環境で動くようにする」という大前提の目的を達成するために、代替の機能を調べたり、ライブラリのドキュメントやソースコードを読み解いていくことの重要性を再認識しました。&lt;/p&gt;

&lt;p&gt;中には根本解決ができない問題もありましたが、「どうすれば最短で大前提の目的を達成できるか？」を意識し、回避策を考えることもできました。&lt;/p&gt;

&lt;p&gt;ライブラリのリプレイスという一見すると地味な対応でしたが、多様な学びを得ることができました。&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Google Cloud Associate Cloud Engineerに合格して得られたもの</title>
    <link rel="alternate" href="https://tech.pepabo.com/2024/02/08/google-cloud-ace-exam/"/>
    <id>https://tech.pepabo.com/2024/02/08/google-cloud-ace-exam/</id>
    <published>2024-02-07T15:00:00+00:00</published>
    <updated>2024-02-24T00:04:00+00:00</updated>
    <author>
      <name>inoway</name>
    </author>
    <content type="html">&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;

&lt;p&gt;こんにちは、minne事業部 Webアプリケーションエンジニアの&lt;a href="https://twitter.com/inoway46"&gt;@inoway&lt;/a&gt;です。2023年12月にGoogle Cloud Associate Cloud Engineer（ACE）に合格しました。本記事では体験談を元に、WebアプリケーションエンジニアがGoogle Cloud 認定資格を受験するメリットについて書いていきます。&lt;/p&gt;

&lt;h2 id="google-cloud-aceとは"&gt;Google Cloud ACEとは&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/learn/certification/guides/cloud-engineer?hl=ja"&gt;Google Cloudの認定試験ガイド&lt;/a&gt;には以下のような記述があります。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Associate Cloud Engineer は、アプリケーションとインフラストラクチャのデプロイと保護、複数のプロジェクトで実行されるオペレーションのモニタリング、エンタープライズ ソリューションの保守を行い、ターゲットのパフォーマンス指標が達成されるようにします。また、パブリック クラウドとオンプレミス ソリューションの実務経験があり、Google Cloud 上での Google マネージド サービスまたはセルフマネージド サービスを活用するデプロイ済みソリューションの管理とスケールにおいて、一般的なプラットフォームベースのタスクを実行する Google Cloud コンソールとコマンドライン インターフェースを使いこなすことができます。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;意訳すると、Google Cloudサービスの運用において、基本的な操作ができるようなレベルを想定して作られていそうです。&lt;/p&gt;

&lt;h2 id="なぜ受験しようと思ったのか"&gt;なぜ受験しようと思ったのか&lt;/h2&gt;

&lt;p&gt;きっかけは、社内でProfessional Data Engineer（PDE）・Professional Machine Learning Engineer（PMLE）の資格取得を目指す人の募集があり、手を挙げたことでした。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudskillsboost.google/paths/?locale=ja"&gt;Google Cloud Skills Boost&lt;/a&gt;で学習を開始しましたが、Google Cloudの運用経験がない上に、関連サービスの概要や基本的なクラウドサービスの知識が皆無の状態だったため、全く内容についていけず・・・という状態でした。&lt;/p&gt;

&lt;p&gt;そこでまずはGoogle Cloudのサービス概要から理解すべきだと思い、ACEの受験を決めました。&lt;/p&gt;

&lt;h2 id="受験してよかったこと"&gt;受験してよかったこと&lt;/h2&gt;

&lt;p&gt;学んでみた感想として、Compute Engine、BigQuery、Cloud Functionsをはじめとする主要サービスの概要や権限管理における思想などを広く浅く網羅しているような印象でした。また、Kubernetesの基礎についても学ぶことができました。&lt;/p&gt;

&lt;p&gt;minneではBigQuery、Cloud Functions、Cloud Pub/Sub、Airflow、Kubenertesなどを活用しているため、これらの概要について理解できたのは大きかったです。&lt;/p&gt;

&lt;p&gt;また、クラウドインフラって面白いなということに気づけたのがよかったです。普段はアプリケーションコードと向き合っていることが大半で、何らかの問題を解決する時にもアプリケーションレベルでしか考えられていなかったのですが、クラウドインフラの知識が増えたことで、「こんなこともできるんじゃないか？」と発想の幅が広がった感覚があります。ただ、まだまだ実務で提案できるような状態ではないので、引き続き学習を進めていきたいです。&lt;/p&gt;

&lt;h2 id="合格するためにやったこと"&gt;合格するためにやったこと&lt;/h2&gt;

&lt;p&gt;学習には書籍とUdemyとChatGPTを使用しました。学習期間は約2ヶ月です。&lt;/p&gt;

&lt;p&gt;実際に手を動かしながら学びたい場合は、&lt;a href="https://www.cloudskillsboost.google/paths/?locale=ja"&gt;Google Cloud Skills Boost&lt;/a&gt;を活用するのがよいと思います。&lt;/p&gt;

&lt;h3 id="書籍"&gt;書籍&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://book.impress.co.jp/books/1122101107"&gt;『徹底攻略 Google Cloud認定資格 Associate Cloud Engineer教科書』&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;図解が豊富で、内容もクラウドインフラ初学者でも理解できるように書かれていてよかったです。各章末に模擬問題がついていて、学んだことが定着しやすい構成になっています。筆者はこの書籍を2周しました。&lt;/p&gt;

&lt;h3 id="udemy"&gt;Udemy&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.udemy.com/course/google-cloud-associate-cloud-engineer4200/"&gt;『これだけで合格！Google Cloud Associate Cloud Engineer模擬試験問題集（4回200問）
』&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4回分の模擬試験があり、それぞれ1回ずつやりました。書籍を1周した段階で正解率が50%程度だったので、2周読むことにしました。2周した後に解いてみると正解率がすべて70%を超えたので、このタイミングで受験しようと決めました。&lt;/p&gt;

&lt;h3 id="chatgpt"&gt;ChatGPT&lt;/h3&gt;

&lt;p&gt;書籍やUdemyの模擬試験の解説を読んで分からなかった点をChatGPTに質問していました。個人的な印象ですが、ChatGPTの回答には論理の飛躍がなく、必要な前提知識を補いながら順を追って解説してくれるため、資格試験の学習にとても役立ちました。&lt;/p&gt;

&lt;h2 id="試験当日の感想"&gt;試験当日の感想&lt;/h2&gt;

&lt;p&gt;テストセンターか遠隔監視オンライン試験（リモート）のどちらかを選べますが、静かで慣れた環境で受験したかったため、オンライン試験を選択しました。周囲の物を撤去しないといけないので少し準備が大変でしたが、無事に試験を開始できてよかったです。&lt;/p&gt;

&lt;p&gt;約50問の出題に対して試験時間は120分なので、1問あたりにかけられる時間は2分ほどです。Google Cloudのサービスや思想について正しく理解した上で、考えて答えないといけない内容がほとんどだったため、35問目あたりから頭が回らなくなった記憶があります。疲れたら1分ほど目を閉じて休むのがよい、というアドバイスが試験開始直前の案内にあったので実践したところ、頭が再び回り始め、解き終えることができました。&lt;/p&gt;

&lt;p&gt;回答送信後、すぐに合否が出るので、合格の文字が表示された時はホッとしました。ただ、正式な合格通知は、不正などがなかったか確認の上、後日送られてくるため、必ず合格になるというわけではなさそうでした。&lt;/p&gt;

&lt;h2 id="おわりに"&gt;おわりに&lt;/h2&gt;

&lt;p&gt;ACEに合格した今、次はPDEといきたいところですが、インフラ関連の知識が不足している状態ではあまり身にならない気がしたため、LPIC-1の学習を進めています。また、知識があっても実務で活用できなければ「できる」とは言えないので、積極的に実務でインフラ周りに関わっていきたいと思います。最後まで読んでいただきありがとうございました！&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>WordPress有料テーマのセキュアな配布機能の実現</title>
    <link rel="alternate" href="https://tech.pepabo.com/2024/02/05/wp-theme-and-archives-server/"/>
    <id>https://tech.pepabo.com/2024/02/05/wp-theme-and-archives-server/</id>
    <published>2024-02-04T15:00:00+00:00</published>
    <updated>2024-02-24T00:04:00+00:00</updated>
    <author>
      <name>kinosuke01</name>
    </author>
    <content type="html">&lt;p&gt;こんにちは。ホスティング事業部 事業開発チームの &lt;a href="https://twitter.com/kinosuke01"&gt;@kinosuke01&lt;/a&gt; です。夜更かしグセがひどいので、今年の抱負を「きちんと寝る」に設定したのですが、なかなか達成が難しいです。&lt;/p&gt;

&lt;p&gt;さて、少し前の話ですが、&lt;a href="https://lolipop.jp/"&gt;ロリポップ！レンタルサーバー&lt;/a&gt;では、2023年3月に&lt;a href="https://lolipop.jp/info/news/7416/"&gt;選りすぐりのWordPressの有料テーマを購入できる機能&lt;/a&gt;をリリースしました。「どのテーマがいいかわからない！」というユーザーの思いに少しでも答えられればと思い、開発を行ったものです。&lt;/p&gt;

&lt;p&gt;この記事では、有料テーマ取り扱いに伴って必要になったセキュアな配布機能の紹介をしたいと思います。&lt;/p&gt;

&lt;h2 id="背景と要件"&gt;背景と要件&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://wordpress.org/"&gt;WordPress&lt;/a&gt;は、ブログやWebサイトを作る事ができるCMSです。WordPressには無料のものから有料のものまで多種多様なテーマがあり、zipファイルで配布されているものを、サイトにインストールして使用します。&lt;/p&gt;

&lt;p&gt;また、ロリポップには、このWordPressをホスティングサーバーに簡単にインストールできる機能があります。ユーザーはホスティングサーバーへのファイル設置やデータベースの設定などを行うことなく、ロリポップのコントロールパネルを操作するだけで、WordPressの利用を開始できる便利機能です。&lt;/p&gt;

&lt;p&gt;今回、有料テーマの取り扱いに伴い「購入済の有料テーマがある場合は、それを選択してインストールすることで、最初からテーマが組み込まれた状態でWordPress製のサイトを利用することができる」という機能を簡単インストールに追加する必要がありました。&lt;/p&gt;

&lt;h2 id="簡単インストールの仕組み"&gt;簡単インストールの仕組み&lt;/h2&gt;
&lt;p&gt;WordPress簡単インストールの仕組みについて説明します。有料テーマ取り扱い"前"の設計はこうなっていました。&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/05/wp-theme-and-archives-server/images/kantan-install-flow.png" alt="WordPress簡単インストールの流れ" /&gt;&lt;/p&gt;
&lt;p class="pager__caption"&gt;WordPress簡単インストールの流れ&lt;/p&gt;

&lt;p&gt;内部で&lt;a href="https://make.wordpress.org/cli/"&gt;wpcli&lt;/a&gt;というWordPressのCLIツールを用いて、WordPress本体やテーマのダウンロードを行いインストールを実行しています。ここでWordPress本体やテーマのダウンロード元となっているのが「アーカイブサーバー」です。アーカイブサーバーは、静的ファイルを返すだけのシンプルなWebサーバーで、各種zipファイルをホストしており、ダウンロードに関してのアクセス制限はありません。&lt;/p&gt;

&lt;h2 id="有料テーマ対応における課題"&gt;有料テーマ対応における課題&lt;/h2&gt;
&lt;p&gt;ここで「簡単インストール時に有料テーマを同時にインストール」の実現方法について考える必要があります。&lt;/p&gt;

&lt;p&gt;すぐに思いつくのは、アーカイブサーバーに有料テーマのzipファイルを設置する方法です。これならすぐに実現できそうです。&lt;/p&gt;

&lt;p&gt;しかし、そうすると問題ありました。購入したユーザーのみが利用できるテーマのzipファイルのはずなのに、URLがもし分かってしまったら誰でも何回でもダウンロードできてしまうのです。&lt;/p&gt;

&lt;p&gt;何らかのアクセス制限を検討する必要がありました。&lt;/p&gt;

&lt;h2 id="アクセス制限の方法を決める"&gt;アクセス制限の方法を決める&lt;/h2&gt;
&lt;p&gt;アクセス制限には、Bearer認証やBasic認証など様々な方法があります。&lt;/p&gt;

&lt;p&gt;まず、wpcliで対応している認証方法はあるのか、&lt;a href="https://developer.wordpress.org/cli/commands/"&gt;公式ドキュメント&lt;/a&gt;を読んでみたのですが、そのような情報は見当たりませんでした。&lt;/p&gt;

&lt;p&gt;仕方がないので、wpcliやwpcliがincludeしているWordPressのコードを読んだり、実験することで確認を行いました。その結果わかったのは、Basic認証は使えず、HTTPヘッダの指定不可でBearer認証も使えないということでした。しかし、幸いにもURLに含まれるクエリパラメータは使えることはわかりました。&lt;/p&gt;

&lt;p&gt;これを踏まえて、クエリパラメータに有効期限つきのトークンを付与した署名付きURLを採用することにしました。&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## 署名付きURLの例
https://example.com/wordpress/wordpress-x.x-ja.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzU3NzM2NTIsImlhdCI6MTY3NTc3MzU5Miwic3ViIjoicHJvdGVjdGVkL3dvcmRwcmVzcy90aGVtZXMvbmV3LXN0YW5kYXJkLTQuMC4zLnppcCJ9.NHDTRZ1lR5WnBAc6d2cBiKe3hReAQ5OnxWqUZjRWDUs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="構成を考える---オブジェクトストレージを使う案"&gt;構成を考える - オブジェクトストレージを使う案&lt;/h3&gt;
&lt;p&gt;ここから、署名付きURLを実現する構成を考えていきます。すぐに思いつくのは、パブリッククラウドのオブジェクトストレージを使う方法です。&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/05/wp-theme-and-archives-server/images/flow-idea01.png" alt="オブジェクトストレージを使う案" /&gt;&lt;/p&gt;
&lt;p class="pager__caption"&gt;オブジェクトストレージを使う案&lt;/p&gt;

&lt;p&gt;既存のアーカイブサーバーをオブジェクトストレージに置き換えて、APIから署名付きURLを取得し、取得したURLからダウンロードを行います。さらに、パブリッククラウドのオブジェクトストレージはマネージドなサービスなので、保守の手間がかからないメリットもあります。&lt;/p&gt;

&lt;p&gt;しかし、これには１点問題がありました。コストの問題です。&lt;/p&gt;

&lt;p&gt;簡単インストールが実行されるほど、オブジェクトストレージからインターネットへのデータ転送送信量が増えるため、その分従量課金されてしまいます。試算してみたところ、対策を検討したほうがよさそうでした。&lt;/p&gt;

&lt;h3 id="構成を考える---オブジェクトストレージの前段にキャッシュサーバーを置く案"&gt;構成を考える - オブジェクトストレージの前段にキャッシュサーバーを置く案&lt;/h3&gt;
&lt;p&gt;オブジェクトストレージからのインターネットへのデータ転送送信量を減らすために、その前段にキャッシュサーバーを設ける方法を検討しました。&lt;/p&gt;

&lt;p&gt;&lt;img src="/blog/2024/02/05/wp-theme-and-archives-server/images/flow-idea02.png" alt="オブジェクトストレージの前段にキャッシュサーバーを置く案" /&gt;&lt;/p&gt;
&lt;p class="pager__caption"&gt;オブジェクトストレージの前段にキャッシュサーバーを置く案&lt;/p&gt;

&lt;p&gt;キャッシュサーバーに署名付きURLを払い出すAPIを実装します。そのAPIから署名つきURLを取得し、そのURLからダウンロードを行う形式です。署名付きURLの向き先はキャッシュサーバ-
で、キャッシュがあればそのファイルを返し、キャッシュがなければオブジェクトストレージからファイルを取得しキャッシュしたうえでファイルを返す構成で考えています。&lt;/p&gt;

&lt;p&gt;この構成で試算したところ、オブジェクトストレージにかかる料金を大幅に削減することができました。よさそうです。&lt;/p&gt;

&lt;h2 id="実装してリリースする"&gt;実装してリリースする&lt;/h2&gt;
&lt;p&gt;ここまでの検討を踏まえて実装を行いました。&lt;/p&gt;

&lt;p&gt;キャッシュサーバーの実装はGo言語で行いました。また署名つきURLに付与するトークンは、ステートレスで管理がラクという理由からjwtを使用しています。&lt;/p&gt;

&lt;p&gt;また、キャッシュサーバーは保守性を考慮して、Kubernetesクラスター上に構築しました。キャッシュはPod内のファイルシステムに保存します。一度キャッシュしたファイルが更新されることはほぼないので、有効期限は設けていません。もし仮にキャッシュファイルをパージしたい場合は、ロールアウトをするだけで済みます。&lt;/p&gt;

&lt;p&gt;そして、無事リリースに至ることができました。&lt;/p&gt;

&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;p&gt;WordPress有料テーマ取り扱いに伴うアーカイブサーバーの刷新について、紹介しました。&lt;/p&gt;

&lt;p&gt;この記事を書いているときにも、取り扱っているWordPress有料テーマが増えました。どのテーマがいいか悩んでいる方の道しるべになれば、嬉しいです。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://lolipop.jp/media/emanon/"&gt;ロリポップのWordPress有料テーマに新たに「Emanonシリーズ」の3種が仲間入りしました！&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://lolipop.jp/media/stork19/"&gt;ロリポップのWordPress有料テーマに新たに「STORK19」が仲間入りしました！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
  </entry>
</feed>
