Wiz テックブログ

Wizは、最新のIoTやICTサービスをお客様に届ける「ITの総合商社」です。

新卒がタスクの恐怖で殺されないためには(仮)

f:id:daisuke_john:20210916160830p:plain

こんにちは、フロントエンドの益田です。
OJT期間を終えてタスク管理について相談する機会があったので、今回は入社して5ヶ月経った自分なりのタスクの進め方について書いていきたいと思います。

1. 期限を確認する

まずは、緊急か緊急でないかの確認をして、緊急を要する要件でないのならTODOリストに入れて一旦忘れるようにしています。
人によって管理の仕方が違うと思いますが、私はノートに殴り描きしてからリスト化するようにしています。

2. 日数がかかるタスクは定期的にハートビートを送る

大きなタスクや並行しているタスクがある場合は進捗を報告をして、一人で悩む時間を一定以上掛けないようにしています。(生存報告のようなもの)

  • ハートビート:心臓の鼓動、拍動、心拍という意味

3. なるべく早く反応する

ディレクターやデザイナーさんとのやり取りや、差し込みでタスクが降ってきた時にすぐに回答できない場合などは、特に以下の2つを意識するようにしています。

1. すぐに対応できない場合は期限を切って対応する。
2. ここではあくまで「反応」するだけ。
(あなたのメッセージはたしかに受け取りましたよ!と返すだけ)

メリットとしては、友達とのLINEでも同じだと思いますが相手の安心感が得られることと自分の未着手のタスクが1個減ります。

4. 初回レビューのタイミングは予め決めておく

タスクに着手する前から「ざっくりこれぐらい作って、いったんレビューしてもらう」という計画を立てて進めています。 早めのタイミングにレビューをして貰うことで後半になっての修正が入った場合に対応しやすくなったと感じています。
通常業務以外にも目標ややりたいことリストなどの振り返りにも活用でき、2週間や月1などスケジュールを決めて定期的な見直しをしてフィードバックを貰っています。

5. タスクを大きい粒度と小さい粒度に分けて管理する

工数分かりづらいタスクや期間が長いタスクがある場合には、Notion, ノート, リマインダーを使い分けて行なっています。
とりあえずノートに書き出してそこからNotionやリマインダーに設定をして管理しやすいようにしています。

  • ノート : 殴り書き、情報・思考の整理に使う
  • Notion : タスクの進捗。工数見積もりなど
  • リマインダー : 細かい作業など

6. 時間で区切る

フィードバックで貰う事が多かったのが「時間で区切る」でした。
1つのタスクを終えて次に取り掛かりたいタイプなので苦手な部分ではあるのですが、時間(タイムボックス)で区切ることで並行しながらタスクを進められ、自分のタスクが計測しやすいので工数が見積もりづらいという部分もカバーされていくと思います。

タイムボックス
 タスクに合わせて時間を決めるのではなく、時間を固定する手法を、「決まったサイズ(時間)の箱にタスクを入れる」というイメージでタイムボックス化(timeboxing)と言います。
エンジニアの知的生産術 ――効率的に学び,整理し,アウトプットする:書籍案内|技術評論社

7. マインドマップを描いて状況を俯瞰する

現状のタスクを洗い出したり、タスクの詳細を整理する時に使っています。 メリットとして、文章が苦手な人でも単語で書けるので楽に始められることと、朝の5分〜10分程度ノートに書き出して気持ちの整理と優先順位付けを行うことでスッキリした状態で始められるのでお勧めです。派生して書き出せるので、企画でのアイディア出しにも使っています。

その他Wizでやっていること

KPT

Wizでは隔週で1on1を行なっておりその中で進捗と以下のKPT(Keep, Problem, Try)を使ってフィードバックをしてもらっています。次週に向けての改善点も見えてくるので細かく洗い出して振り返る時間を作っています。  f:id:daisuke_john:20210916152823j:plain

雑談会

コミュニケーション1on1以外にも他職種も含めた雑談会を月に1回程度行なっています。 デザイナーやディレクター・ライターの方も参加して頂けるので、業務での相談なども気軽にできるようになったと感じています。
雑談会の詳細については下記の記事に記載していますので気になった方は是非ご覧下さい。
コロナ禍での他職種とのコミュニケーションについて - Wiz テックブログ

参考

スケジュールにバッファを設けるのは悪か? - ユニファ開発者ブログ

【新人ITエンジニア向け】僕が仕事をする上で大事にしているポイントあれこれ - give IT a try

おわりに

今回は入社して5ヶ月経って個人的に役に立ったことを書いてみました。
現状、模索段階ということもあり自分に合ったやり方を今後見つけていきたいと思います。

また、

Wizではエンジニアを募集しております。

興味のある方、ぜひご覧下さい。

careers.012grp.co.jp

php-cs-fixerをつかったコードフォーマットの自動化(git hook)

こんにちは、バックエンドエンジニアの三井です。

社内でコードフォーマットを統一する活動を行っており、phpのパッケージを使ったフォーマッターの導入と、git hookを使ったフォーマットの自動化に成功したので概要をまとめていきたいと思います!

導入したphpパッケージ


今回導入したパッケージはphp-cs-fixerです。

導入環境

laravel: 8.60.0
composer: 2.1.6
php: 8.0.9

概要

composer で導入できるphpのパッケージです。導入後コマンドを打つことで任意のファイルにフォーマッターをかけることができます。
フォーマットルールについて、PSR12をはじめ多くのフォーマットに対応しており、設定ファイルを作成することでオリジナルのフォーマットルールを生成することができます。
導入時はPSR12のみを導入して運用してましたが、今後社内でフォーマットを統一するためにルールの取り決めも行っていきたいと思っております!

導入方法・設定


インストール

今回は私の動作環境であるlaravelプロジェクト内での使用を想定して行います。

# パッケージのインストール
composer require friendsofphp/php-cs-fixer
# 設定ファイルをプロジェクトディレクトリに追加
touch .php-cs-fixer.dist.php
# gitignoreにキャッシュファイルを追加
.php-cs-fixer.cache

設定ファイル

設定ファイル.php-cs-fixer.dist.php内を以下のように記述します。 ルールはここで確認することができ、PSR12等複数のルールをまとめたルールセットを指定することも可能です。

<?php
/*
 * This document has been generated with
 * https://mlocati.github.io/php-cs-fixer-configurator/#version:3.1.0|configurator
 * you can change this configuration by importing this file.
 */
$config = new PhpCsFixer\Config();
return $config
        ->setRiskyAllowed(true)
    ->setRules([
        '@PSR12' => true, // ここにルール追記
    ])
    ->setFinder(PhpCsFixer\Finder::create()
        ->exclude([ // 除外ファイル
              'vendor'
        ]) 
        ->in(__DIR__)
    )
;

setRiskyAllowed() : riskyなルール(コードを書き換える破壊的な修正を含むルール)を有効にするかを設定します。
setRules() : 使用したいルールをここで列挙します。
setFinder()...->exclude(['']) : 除外したいディレクトリ名を列挙することでそのディレクトリ内のファイルは無視されます。

ルール設定方法

ルール生成用のツールで生成ファイルを作ることができます。

https://mlocati.github.io/php-cs-fixer-configurator/#version:3.1

f:id:mtimti:20210915192833p:plain

このページでは使用可能なルールが列挙されており、ページ内の歯車マークをクリックすると使用するルールの選別ができるようになります。

f:id:mtimti:20210915193249p:plain

またその状態で+ボタンを押すことでルールセットの指定も可能です。 最後にExportボタンを押すことで設定ファイルの出力ができます。

f:id:mtimti:20210915193037p:plain

f:id:mtimti:20210915193201p:plain

実行コマンド

# version バージョン確認
$ ./vendor/bin/php-cs-fixer --version

# dry-run (修正はされないが、--diffオプションで修正前後の確認ができる)
$ ./vendor/bin/php-cs-fixer fix -v --diff --dry-run

# fix 実際に修正実行
$ ./vendor/bin/php-cs-fixer fix -v --diff
--dry-run # 変更なし実行
--diff # 差分出力
--allow-risky # riskyルールの許可

git hookの概要


詳しい説明は割愛しますが簡単に説明すると、gitのコミット等のコマンドの直前・後にスクリプトを実行できるという仕組みです。 今回は"コミット直前にフォーマッターを実行し、修正後コミットする"という機構を作ります。 隠しディレクトリ.gitの配下にhooksディレクトリを作りpre-commitファイルを作成します。

#!/usr/bin/env bash

#ルートの設定(laravel projectの配下を指定)
ROOT=$(git rev-parse --show-toplevel)/projdir 

PHP_CS_FIXER="./vendor/bin/php-cs-fixer"
HAS_PHP_CS_FIXER=false

if [ -x $ROOT/vendor/bin/php-cs-fixer ]; then
    # パッケージの存在チェック
    HAS_PHP_CS_FIXER=true 
fi

LIST=$(git status | grep -e '\(modified:\|new file:\)'| grep '\.php')
if $HAS_PHP_CS_FIXER; then
    for file in $LIST
    do 
  # --dry-runオプションで先にエラーチェック
        ./projdir/vendor/bin/php-cs-fixer fix $file --diff --dry-run --path-mode=intersection --config=$ROOT/.php-cs-fixer.dist.php >/dev/null 2>&1; 
        ERR=$?
        ERRCNT=0
  # ステータスコードが0(修正なし)か8(修正あり)以外は構文エラー等に該当するのでエラーメッセージを表示
        if [ $ERR != 0 -a $ERR != 8 ]; then
            ERRCNT=$((ERRCNT+1))
            echo "php-cs-fixer-$ERR 構文エラーが検出されました $file"
            echo "error $ERRCNT"
        else
   # 修正実行
            ./projdir/vendor/bin/php-cs-fixer fix $file --path-mode=intersection --config=$ROOT/.php-cs-fixer.dist.php >/dev/null 2>&1;
            git add "$file";
        fi
    done
        if [ $ERRCNT -gt 0 ]; then
            echo "php-cs-fixer $ERRCNT 件構文エラーが検出されました"
            exit 1
        fi
            echo "php-cs-fixer フォーマット修正が完了しました"
else
    echo ""
    echo "Please install php-cs-fixer, e.g.:"
    echo ""
    echo "composer require --dev fabpot/php-cs-fixer:dev-master"
    echo ""
    exit 1
fi

これでcommit時にこのスクリプトが自動実行され、フォーマッターが動きます。

参考にさせていただいたソース

実際に開発で運用してみた感想


今回フォーマッターの統一に至った経緯としては社内で使用エディタが統一されていなかったため、各々のフォーマットでコーディングされていたという状況を受けてのことでした。
実際に使ってみて思ったこととしては、エディタ内のフォーマッターと合わせて運用すると良いかもしれない、ということです。コミット直前に修正を行うデメリットとして、コードを書いている途中で整ったコードを見れないという点です。commit内容としては整っているコードですが、"実装中にコードを読みやすくする"にすることが目的なら本末転倒だと思います。そのため、"自分がわかりやすいように"エディタのフォーマッターも兼ねて運用するのも有効だと思いました。

Laravelで初めてのテストとTDDをサクッとやってみる【初心者向け】

TDDのイメージ

こんにちは、バックエンドエンジニアの中嶋です。

今年の春に入社して主にLaravelを扱って開発をしていますが、

プロダクト開発はTDD(テスト駆動開発)で行うことが多くなってきました。

もちろん規模や目的によってTDDを導入すべきかは検討が必要ですが、

テストなんて面倒だしそもそも何からやって良いかわからない」という心理的なハードルがあることで導入していないパターンも割と多いのではないでしょうか?

そこで今回は、

  • Laravelを用いてまだテストコードを書いたことがない
  • TDD(テスト駆動開発)を実践したことがない
  • 気にはなるけど実践するのはハードルが高くて..

という方に向けてTDDの雰囲気だけでも知ってもらうべく、その手順を簡単に紹介したいと思います。

今回やること

  • PHPUnitを用いてLaravelで実装した簡易APIのFeatureテストを手軽に行う
  • GETでアクセスすると保存されているデータをJSON形式で返すというシンプルなもの

  • 手軽に試して雰囲気をお伝えするのが目的なので、登録処理やデータベースのテストはやらない

↓もし「APIって何?」という方はこちらに簡単に纏めているので良ければご覧ください
REST APIとは?ざっくりと理解してみる【初心者向け】 - Wiz テックブログ

TDD(テスト駆動開発)とは..

テスト駆動開発はその名の通りテストを中心とした開発手法のことです。

Test-Driven Development」の頭文字を取ってTDDと呼ばれることが多く、

XP(エクストリームプログラミング)の考案者であるケント・ベック氏が編み出した手法とされています。

概要をざっくり一言でまとめると、

ちゃんと動く状態を保ちながら機能を素早く実装し、きれいなコードを目指してリファクタリングしていく

です。

そのプロセスとしては

  1. きちんと動作するか?のテストを作成する
  2. テストを実行して失敗することを確認する
  3. コードが汚くても良いので最低限仕様を満たす実装を行う
  4. テストを実行して成功するようになったことを確認する
  5. テストが成功することを確認しながらきれいなコードにリファクタリングしていく TDDのフロー

というような流れになります。

コツとしては「テスト作成」「機能実装」「リファクタリング」を、それぞれ最小の単位で素早く行うことです。

テストや機能を素早く実装し、テストが常にグリーンの状態になる..このテストに守られている安心感があると、開発のリズムも良くなってくるはずです。

逆にテストなしに大きな単位で実装やリファクタリングを行うと、

「あれ?なんで動かないんだ?」「どこで間違えたんだ?」ということに時間や思考を取られるので、

テストに守られた状態になれるというだけでもかなり恩恵が得られると思います。

テストの手順

さて、概要がわかったところで「PHPUnit」を用いたテストの作成をしていきます。

PHPにおけるテストはこのPHPUnitデファクトスタンダード(事実上の標準)と言えると思います。

そしてLaravelでもあらかじめ用意されており、そのまま使用することができます。(必要に応じて設定は必要です)

(Tips) 設定ファイルについて

Laravelのプロジェクトディレクトリ直下にphpunit.xmlというファイルが用意されています。

これはPHPUnitのテスト実行の設定ファイルで、

今回のような簡易なテストではデフォルトのまま実行することができますが、テストケースをグループ化したりテスト用のデータベースを用意する場合など必要に応じて編集します。

【手順①】テストしたいことリストを作成

まずは「どんな機能をテストしたいか」をリストアップします。

今回の例では「お知らせAPI」という新着ニュースのデータを返す簡易なAPIをテストしたいので、

  • 'api/notifications' というエンドポイントにGET メソッドでアクセスできる(ステータス200を返す)
  • 「お知らせ」データがjson形式で取得できる
  • 取得したJSONデータがデータベースに登録されている値と一致する ..

といった項目などがリストアップできると思います。

これはテストコードの中にコメントアウトとして書く程度でもOKです。(今回はGETでアクセスできるかまで検証します)

【手順②】 テストクラスを作成

実際のテストコードを作成するにはartisanのmakeコマンドを使います。

今回の例ではNotificationApiTestというクラスを作成します。

$ php artisan make:test NotificationApiTest

これでTests/Feature/NotificationApiTest.phpが雛形として生成されました。

(Tips) FeatureテストとUnitテストについて

このように特にオプションを付けないで実行すると、Featureテストのディレクトリにファイルが生成されます。

tests
├── CreatesApplication.php
├── Feature
    ├── ExampleTest.php
    └── NotificationApiTest.php
├── TestCase.php
└── Unit
    └── ExampleTest.php

これはTests\TestCaseクラスを継承していて、LaravelがPHPUnitを拡張したものになります。

Laravelの機能を利用したテストを行う場合はこちらを用いるのが良いと思います。

一方、上記コマンドで--unitというオプションを付けて実行するとtests/Unitディレクトリにファイルが生成されます。

こちらはPHPUnit\Framework\TestCaseクラスを継承しており、Laravelの機能を利用しない範囲で粒度の細かいテストを行う場合はこちらを用いると良いと思います。

【手順③】 失敗するテストを書く

先に述べた手順のとおり、機能実装する前にテストを作成します。

本来はテスト用のデータベースやもっと細かいテスト項目を用意すべきですが、今回はお試しなので簡易な格好としています。

・Tests/Feature/NotificationApiTest.php

<?php
..
class NotificationApiTest extends TestCase
{

    /**
     * @test
     */
    public function GETで正常にアクセスできるか()
    {
        // エンドポイントにGETでアクセス
        $response = $this->get('/api/notifications');

        // ステータスコード200が返るか
        $response->assertStatus(200);

    }

テストメソッドの内容としては、エンドポイントにGETでアクセスする擬似リクエストを作成し、

その結果をアサーションでチェックしています。

(Tips) テストメソッドの名前について

今回のテストメソッド名は日本語になっていて違和感のある方も多いと思います。

これは@testノーテーション(/**の部分)を設けていることで、メソッド名に日本語を用いても問題なく実行できるというものです。

@testノーテーションを使わない場合はメソッド名をtestFoo()のようにする必要があるのですが、

どちらも動作は同じなので、メソッド名を毎回考えるのが面倒なのと日本語の方がテスト結果を見た時にも直感的でわかりやすいという理由で、 個人的には@testノーテーションを使用してメソッド名は日本語で定義することが多いです。

(Tips) assertメソッドについて

assertメソッドは「テストが成功したか」「失敗したか」をチェックして結果を返すものです。

テストコードを書くにあたってはこのassertメソッドをいかに知っているかが大事だったりします。

これは本当にたくさんの種類があるので具体的な紹介は公式ドキュメントに譲りたいと思いますが、

今回のようなAPIのテストにおける代表的なものを少しだけ紹介すると以下のようなものがあります。

メソッド テスト内容
assertSuccessful() HTTPステータスコードが200番台なら成功
assertStatus($status) HTTPステータスコードが$statusと一致していれば成功
assertJson(array $data, $strict=false) レスポンスされたjsonの配列に$dataが含まれていれば成功
assertExactJson(array $data) レスポンスされたjsonの配列が$dataと一致していれば成功
【手順④】 テスト実行 →失敗する

最低限のテストを作成したら、まずは実行して失敗することを確認します

vendor/bin/phpunit tests/Feature/NotificationApiTest.php
PHPUnit 8.2.5 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 1.51 seconds, Memory: 30.00 MB

There was 1 failure:

1) Tests\Feature\NotificationApiTest::GETで正常にアクセスできるか
Response status code [404] does not match expected 200 status code.
Failed asserting that false is true.

/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:89
/var/www/html/tests/Feature/NotificationApiTest.php:39

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

当然ですが、エンドポイントを実装していないのでステータスコード404(NotFound)が返り失敗します。

【手順⑤】 最低限の実装 →テストを成功させる
エンドポイントを定義

404が返っているので、まずはエンドポイントを定義します。

api.php

<?php
..
Route::get('notifications', 'Api\NotificationApiController@index')->name('notifications');
もう一度テストを実行
PHPUnit 8.2.5 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 2.58 seconds, Memory: 30.00 MB

There was 1 failure:

1) Tests\Feature\NotificationApiTest::GETで正常にアクセスできるか
Response status code [500] does not match expected 200 status code.
Failed asserting that false is true.

/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:89
/var/www/html/tests/Feature/NotificationApiTest.php:39

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

こちらも当然ですが失敗します。

今度は404ではなく、ステータスコード500(ServerError)が返りました。

エラー内容はこれではわかりませんが、エンドポイントにはアクセスできたものの処理を実装していないのでサーバーエラーが返る状態ですね。

APIの処理を実装

次にAPIの処理本体を最低限のレベルで実装します。 内容自体は今回は重要ではないので無視していただいてOKです。

・NotificationApiController.php

<?php
..
class NotificationApiController extends Controller
{
    public function index()
    {
        $data = Notification::getNotificationsIndex();
        $status = 200;
        $message = 'Success';

        return ResponseUtil::jsonResponse($status, $message, $data);
    }
}
三度、テストを実行

この状態で改めてテストを実行します。

PHPUnit 8.2.5 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 1.97 seconds, Memory: 30.00 MB

OK (1 test, 1 assertion)

これでようやくテストグリーン(OKの状態)になりました🙌

あとはテストコードを信頼できるレベルに強化(DBも含めてデータが正しいかチェックする等)しつつ、

この繰り返しでテストグリーンを保ちながら、例えばAPI処理をtry〜catchの形にリファクタリングしたり、新たな機能を実装していくことができます。 TDDのフロー

今回は簡易な内容での導入でしたが、

実際の開発でも是非テストに守られた状態で実装していく気持ち良さを体感してみてはいかがでしょうか??

Wizでは良いものをワイワイと作るべく、エンジニアを絶賛募集しています!(ワイズと読みます)

↓↓↓興味ある方はぜひご覧ください!↓↓↓

careers.012grp.co.jp

ドキュメント作成に向いている静的サイトジェネレータはどれ?

f:id:rainymoment0616:20210827092425p:plain

こんにちは、フロントエンドエンジニアの柳田です。

現在、チーム内でドキュメントを作成する機会が多くなってきました。

社内では、様々な方法でドキュメントを作成しているのですが、そのひとつの方法として、静的サイトジェネレータ(Static Site Generator:SSG)があります。

今回は、ドキュメント作成にあたって自分が試してみたSSGをいくつかご紹介したいと思います。

静的サイトジェネレータ(SSG)とは?

静的サイトジェネレータとは、入力ファイルから簡単に静的ページを生成してくれるツールのことです。

有名なものだと、Next.jsやGatsby、Nuxtがありますね。

実際に表示に利用されるものは静的ページのため、動的ページに比べてサイトパフォーマンスが高く、SPAに比べてSEO面で優れています。

一方、大規模サイトのようなページ数の多いものは生成するのに時間がかかったり、更新頻度が多い場合に頻繁に生成を行わなくてはいけないというデメリットもあります。

ドキュメント作成するにあたってのポイント

今回、ドキュメント作成にあたって、下記をポイントに置いています。

  • マークダウン形式で書くことができる(誰もが簡単にドキュメントを書くことができるし、他サービスなどへの移行も簡単)
  • ドキュメントとしての見た目が担保されたデザインである
  • バージョン管理ができる
  • 環境構築が簡単

ドキュメント作成に向いていると感じるSSG

VuePress

https://vuepress.vuejs.org/

Vue.jsベースのドキュメント作成に特化したSSGです。

f:id:rainymoment0616:20210827083538p:plain
VuePressを利用したドキュメントの画面

  • 環境構築が簡単
  • Vue.jsの知識がなくてもマークダウンを利用したドキュメント作成が可能
  • デフォルトのテーマがシンプルかつ、ドキュメント向き
  • GitHub等のバージョン管理ツールでの管理が可能

マークダウンの中で、Vueコンポーネントを利用することができるのも魅力のひとつで、独自に作ったコンポーネントも利用することが可能です。

Docusaurus

https://docusaurus.io/

Facebook製の、Reactベースのドキュメント作成に特化したSSGです(アイコンの怪獣が可愛い)。

f:id:rainymoment0616:20210827083557p:plain
Docusaurusを利用したドキュメントの画面

  • Reactの知識がなくても、マークダウンを利用したドキュメント作成が可能(※)
  • CodeSandBoxやStackBlitzを利用してお試し開発が可能
  • デフォルトのデザインがドキュメント向け
  • GitHub等のバージョン管理ツールでの管理が可能

開発環境を作成する前に、お試しで弄ることができるのは、魅力的なポイントのひとつだと思います。

※一部ページはReactコンポーネントで構成されているため、カスタマイズを行う場合にはReactの知識が少し必要になってきます

Hugo

https://gohugo.io/

GitHubのスター数53000超えの、Go言語ベースのSSGです。

f:id:rainymoment0616:20210827085219p:plain
Hugoを利用したドキュメントの画面

  • ビルド時間が早い
  • マークダウンを利用したドキュメント作成が可能
  • テンプレートデザインが豊富(※)
  • GitHub等のバージョン管理ツールでの管理が可能

テンプレートのデザインがとにかく豊富で、ドキュメント向きのものから、ブログやポートフォリオ、ギャラリーサイトなど、様々なタイプのデザインが揃っています。

(今回、ドキュメント作成にGeekdocというテーマを利用しています。)

※テーマによってはディレクトリ構成が変わったり、独自のコンポーネント記述があるものもあるので、テーマを変更する際は注意が必要です

まとめ

今回はドキュメント作成に向いていると感じたSSGをいくつかご紹介しました。

他にもたくさんあるので、プロジェクトの規模や用途にあったものを試して選んでみてください(『Jamstack.org』で色んなSSGを確認することができますよ)。

株式会社Wizではエンジニアを募集しています!

↓↓↓興味ある方はぜひご覧ください!↓↓↓

careers.012grp.co.jp

PlaywrightとTestCafe比較

f:id:watsonpomelo:20210823183820p:plain

こんにちは、フロントエンドエンジニアの菅野です。

E2Eフレームワーク,ライブラリといえば Selenium,Cypress,Puppeteer,TestCafe,Playwright などたくさんありますが、 今回は私もプロジェクトで導入しているTestCafeと昨年にリリースされたPlaywrightについて簡単に比較してみたのでお話したいと思います。

TestCafe

Playwright

どちらも最新リリースが10日以内(8/23時点)なので活発に開発されています。

PlaywrightはMicrosoftが開発しているので今後開発されなくなるリスクは少なそうに思います。

サポートブラウザ

TestCafe Playwright
Chrome
Safari
Firefox
Opera ×
Edge ○ ※Chromiumのみ
IE11 ×
Chrome mobile
iOS Safari

機能 / 特徴

TestCafe Playwright
ヘッドレスモード
TypeScriptサポート
同時テスト実行
デバッグモード
モバイル実機テスト ×
コード生成 ×

他にもたくさんあるのですがいくつか抜粋して書いてみました。

TestcafeではスマホでURLやQRコードを読み込めば実機上でテストが実行できる機能があり、 Playwrightにはテストケースをちまちま書かなくてもユーザーの操作からコードを生成してくれる機能があったりと、それぞれに特徴や強みがあるのが分かります。

コード / 実行時間

簡単にですがスクリーンショットを撮るコードで違いを見てみたいと思います。

TestCafe

import { Selector } from 'testcafe';

fixture`screenShot`
  .page('https://example.com');
test('top', async t => {
  await t
    .takeScreenshot({
      path: `${t.browser.name}/testcafe/top.png`
    });
});

Playwright

const { chromium } = require('playwright')

(async () => {
  const browser = await chromium.launch({
    headless: false
  });
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: `./screenshots/Chrome/playwright/top.png` });
  await browser.close();
})();

Playwrightではブラウザコンテキストとコンテキストの中でpageを生成する記述があるため、TestCafeと比べるとコードが少し長くなってしまいます。

あとは、browser.close();を書くことでブラウザを閉じ処理を終了するのですが、TestCafeは記述を書かなくても自動でブラウザを閉じてくれるのでそういう細かいところで便利だなと思います。

続いて実行時間を比べてみます。

今回はブラウザUIを表示させて時間を計測したかったので、Playwrightではデフォルトでtrueとなっているheadlessモードをfalseにしています。

TestCafe Playwright
Time 10183.922ms 2313.172ms

実行時間はPlaywrightが7秒以上速い結果となりました。

TestCafeはブラウザが立ち上がるまでに特に時間がかかってしまうのでそこで大きく差が出てしまいました。

まとめ

Playwrightは学び始めたところでまだまだ分かっていない部分も多いのですが、 比較をしてみると互いの長所短所が分かりやすくなりますね。

ユーザー操作からコードを生成してくれるPlaywrightはメンテナンスのしやすさからフォームテストなどのテストケースが多い場合に向いてると思います。

TestCafeはサポートブラウザが充実しているのでスマホ実機、IEなど様々な環境でテストを行いたい場合に選ぶと良さそうです。

プロジェクトによってどういったテストを行いたいのか、検証範囲はどこまでなのかなどがあると思いますので、ぜひそれぞれにあったフレームワーク,ライブラリを選んでみてください。

株式会社Wizではエンジニアを募集しています! ↓↓↓興味ある方はぜひご覧ください!↓↓↓ careers.012grp.co.jp

PHPでコンストラクタを複数定義する【初級〜中級者向け】

f:id:t-yone3:20210813094006j:plain

こんにちは、バックエンドエンジニアの米山です。

突然ですがコンストラクタ、使っていますか?

特に、ビジネスロジックをまとめるクラスを作った時や、似たような処理を行うクラスの初期化に役立つのがコンストラクタです。

今回は、そんなコンストラクタを複数作る手法と、私なりのオススメ度も併せてご紹介しようと思います。

それではいってみましょう〜!

そもそも、「コンストラクタ」って何?

Wikipediaにはこうあります。

コンストラクタ(英: constructor)は、オブジェクト指向プログラミング言語で新たなオブジェクトを生成する際に呼び出されて内容の初期化などを行なう関数あるいはメソッドのことである。

JavaPHPC#に代表されるようなオブジェクト指向言語では、クラスのインスタンスを作成する時に初期化処理を行うことができます。

この初期化処理のことを「コンストラク」と呼んでいます。

具体的にPHPを例に、コードで表すとこんな感じになります。

class SomeClass
{
    // これがコンストラクタ
    public function __construct()
    {
        logger()->debug('初期化処理だよん');
    }
}

class Hoge
{
    public function fuga()
    {
        // クラスのインスタンスを作ると、初期化処理が呼ばれる
        // ログに「初期化処理だよん」が出力される
        $instance = new SomeClass();
    }
}

コンストラクタを複数実装する方法

それでは本題に入りましょう。

コンストラクタを複数実装し、引数の数や内容で処理を分岐させます。
いくつか方法があるので、私なりのオススメ度も併せて記載しておきます。

【オススメ度:★★★☆☆】方法その1:コンストラクタをオーバーライドさせる

PHPでは関数のオーバーロードができません。

似たようなことをやるためには、abstractクラスを用意し継承した先のクラスで「自分自身のコンストラクタを呼ぶ」もしくは「親クラスのコンストラクタを呼ぶ」という分岐になります。

コンストラクタだけ、オーバーライドすることができます。

【余談】オーバーロードとオーバーライド

さて、上記で説明した通りなのですが「オーバーロード」「オーバーライド」は混同しやすい用語なので、解説を挟みます。

私なりに理解した内容で説明すると

  • オーバーロード:同名の、類似メソッド(または関数)の作成
  • オーバーライド:メソッド(または関数)の上書き

です。オーバーロードを実際にコードで表すとこうです。

PHPオーバーロードできないのでJavaで表現します)

class SomeClass {
    // 元のメソッド
    public void SampleMethod() {

    }
    // オーバーロードしたメソッド(返却値が異なる)
    public String SampleMethod() {
        return "hoge";
    }
    // これもオーバーロードしたメソッド(引数が異なる)
    public void SampleMethod(int value) {

    }
}

オーバーライドは下記のようになります(同じくJavaで表現します)

class AbstractSampleBase {
    // 親クラスのメソッド
    public String SampleMethod(String value) {
        return "hoge" + value;
    }
}

class ChildSample extends AbstractSampleBase {
    // 継承先で再定義する
    public String SampleMethod(String value) {
        return "fuga";
    }
}
閑話休題】結局、PHPでコンストラクタをオーバーライドすると・・・?

話を戻して、PHPでコンストラクタをオーバーライドさせると、こうなります。

abstract class SampleBase
{
    public $output;
    // 親クラスのコンストラクタは引数なし
    public function __construct()
    {
        $this->output = 'これは親クラスのコンストラクタです';
    }
}

class ConstructorSampleA extends SampleBase
{
    // 子クラスのコンストラクタは引数あり
    public function __construct($value)
    {
        $this->output = 'これは子クラスのコンストラクタです' . $value;
    }
}

これを、例えばControllerクラスなどで呼び出す場合、以下のようになります。

class SampleController extends Controller
{
    public function index()
    {
        // 親クラスのコンストラクタがよばれる
        $data = new ConstructorSampleA();
        // 下をコメントアウトすると子クラスのコンストラクタが呼ばれる
        // $data = new ConstructorSampleA('hello world');
        return view('sample', ['output' => $data->output]);
    }
}

【オススメ度:★★★☆☆】方法その2:コンストラクタをオーバーライドさせる(1メソッドで分岐)

方法その1と似ていますが、こちらのやり方では必ず子クラスのコンストラクタを呼び、引数の内容によって親クラスのコンストラクタを呼ぶ、というやり方です。

abstract class SampleBase
{
    public $output;
    // 親クラスのコンストラクタは引数なし
    public function __construct()
    {
        $this->output = 'これは親クラスのコンストラクタです';
    }
}

class ConstructorSampleB extends SampleBase
{
    // 子クラスのコンストラクタは引数あり(デフォルトはnull)
    public function __construct($value = null)
    {
        if (empty($value)) {
            // 親クラスのコンストラクタを呼び出す
            parent::__cunstruct();
        } else {
            // 引数の値があった時に子クラス側の処理を行う
            $this->output = 'これは子クラスのコンストラクタです' . $value;
        }
    }
}

【オススメ度:★☆☆☆☆】方法その3:func_num_argsとfunc_get_argsでコンストラクタを分岐する

この方法は、可読性の観点からオススメはしません。また、PHPのバージョンにより挙動が変わる可能性があります。

func_num_args関数でコンストラクタに渡した引数の数を取得・チェックし、引数の数に応じて呼び出すコンストラクタを変える方法です。
分岐先のメソッドに渡す引数はfunc_get_args関数で取得できます。

class ConstructorSampleC
{
    public $output;
    public function __construct()
    {
        $argCount = func_num_args();
        $argList = func_get_args();

        // 受け取った引数の数によって、呼び出すメソッドを分岐
        switch($argCount)
        {
            case 1:
                $this->construct1($argList[0]);
                break;
            case 2:
                $this->construct2($argList[0], $argList[1]);
                break;
            case 3:
                $this->construct3($argList[0], $argList[1], $argList[2]);
                break;
        }
    }

    private function construct1($value)
    {
        $this->output = 'これは引数1つのコンストラクタです' . $value;
    }

    private function construct2($value1, $value2)
    {
        $this->output = 'これは引数2つのコンストラクタです' . $value1 . $value2;
    }

    private function construct3($value1, $value2, $value3)
    {
        $this->output = 'これは引数3つのコンストラクタです' . $value1 . $value2 . $value3;
    }
}

class SampleController extends Controller
{
    public function index()
    {
        // 引数1つの時のコンストラクタ
        $data = new ConstructorSampleC('hello');
        // 引数2つの時のコンストラクタ
        // $data = new ConstructorSampleC('hello', 'world');
        // 引数3つの時のコンストラクタ
        // $data = new ConstructorSampleC('hello', 'world', 'hoge');
        return view('sample', ['output' => $data->output]);
    }
}

【オススメ度:★★★★☆】方法その4:interfaceを使ってクラスで分岐する

方法その1では継承を使いました。「同じような処理を行う」が「初期化処理が異なる」場合はinterfaceが有効です。

この例ではfactory関数を用意し分岐条件に応じてそれぞれの初期化を行う、別々のクラスを返すようにします。
継承を使う場合、継承元・継承先でコンストラクタの分岐を行うことで少々複雑さが増します。

主処理が同じ場合は

  • 初期化を行ってから主処理を行うクラス
  • 初期化を行わず、主処理を行うクラス

にクラスそのものを、分けてしまった方がわかりやすくなるケースがあります。

interface ISample
{
    // 共通で行う主処理
    public function execute();
}

class ConstructorSampleD implements ISample
{
    public function construct()
    {
        logger()->debug('引数が0の時のコンストラクタです');
    }

    public function execute()
    {
        // クラスごとに処理を分けても良いし、共通的な処理を作って呼び出しても良い
    }
}

class ConstructorSampleE implements ISample
{
    public function __construct($value)
    {
        logger()->debug('引数が1の時のコンストラクタです' . $value);
    }

    public function execute()
    {
        // クラスごとに処理を分けても良いし、共通的な処理を作って呼び出しても良い
    }
}

class ConstructorSampleF implements ISample
{
    public function __construct($value1, $value2)
    {
        logger()->debug('引数が2の時のコンストラクタです' . $value1 . $value2);
    }

    public function execute()
    {
        // クラスごとに処理を分けても良いし、共通的な処理を作って呼び出しても良い
    }
}

// クラスのインスタンスを作り出すfactoryクラス
class ConstructorSampleFactory
{
    public static function createSample($mode)
    {
        if ($mode === 1) {
            // 引数が0の時はConstructorSampleDクラスとしてコンストラクタを実行
            return new ConstructorSampleD();
        } elseif ($mode === 2) {
            // 引数が1つの時はConstructorSampleEクラスとしてコンストラクタを実行
            return new ConstructorSampleE('hoge');
        } elseif ($mode === 3) {
            // 引数が2つの時はConstructorSampleFクラスとしてコンストラクタを実行
            return new ConstructorSampleF('hoge', 'fuga');
        }
    }
}

この場合、呼び出し元では下記のようになります。

class SampleController extends Controller
{
    public function index()
    {
        // 引数0の時のコンストラクタ
        $data = ConstructorSampleFactory->createSample(1);
        // 引数1つの時のコンストラクタ
        // $data = ConstructorSampleFactory->createSample(2);
        // 引数2つの時のコンストラクタ
        // $data = ConstructorSampleFactory->createSample(3);
        return view('sample', ['output' => $data->output]);
    }
}

この例ではcreateSample関数の中で、直接コンストラクタに引数を渡していますが、呼び出し元(Controller)から引数を繋げたい場合は、createSample関数へ渡す引数を可変長引数リストにする等で対応可能です。

まとめ

PHPでコンストラクタを複数実装する方法をご紹介しました。

他の言語のようにオーバーロードができないので、少し不恰好になってしまいますが

  • 継承を使い、親クラスと子クラスでコンストラクタを分ける
  • func_num_args関数、func_get_args関数を使って、処理パターンを分岐させる
  • interfaceを使ってクラスそのものを分岐し、それぞれで初期化させる

まとめるとこの3パターンになります。

是非試してみて下さい。


Wizではエンジニアを募集しております。

興味のある方、ぜひご覧下さい。

careers.012grp.co.jp

EMになって雑食になり行動が変わった話

f:id:sevenium:20210816152010j:plain


はじめまして。
エンジニアリングマネージャー(以下EM)の岡本です。
今回は私がプレーヤーからマネージャーになって変化した「行動」について書いていきます。


マネージャーになり、基本的に何にでも興味を示し行動するようになりました。

エンジニアに関係ない記事や本でも興味があれば読んでみたり、
コミュニケーションが苦手なのに積極的に取るようになったり、
課題と感じたらとりあえず何かやってみたり、
と何でも好き嫌いなくまず行動するようになりました。

「こんなのできるのが当たり前じゃん!」と思う方もいると思いますが、
自分自身、性格と合わない行動が多く不思議に思っており、
今回振り返りも兼ねてまとめていきたいと思います。

経歴&自己紹介

私は2016年7月に中途でフロントエンドエンジニアとして入社しました。
2019年4月にマネージャーになるまではフロントエンド開発をメインでしておりました。

基本的に内気で初対面の人や人前で話すのが苦手です。
趣味はゲームやキーボードと完全なインドアタイプです。

こういった人間にEMが務まるのかと心配になりますが、とりあえず振り返っていきましょう!

【振り返り1】ビジネス意識が変わったから?

プレーヤーのときはプロダクトを作ること(コードを書きまくること)が楽しく、
その先にあるビジネス意識は薄く、設計やトレンドの技術に意識が向いていました。
何のために作るのかを理解してプロダクトに落とし込むことはできても、
そのプロダクトの利益を最大化するためにエンジニアとして何をすべきかという考えは殆どありませんでした。

しかしマネージャーとなると組織のパフォーマンスの最大化や、
さらなる効率化がミッションとなってくるため、
徐々にビジネスへの意識というのは高まっていきました。

ビジネスとエンジニアを繋ぐためだったり、
ビジネスインパクトを与えられるエンジニア組織を構築するための勉強をしたりと、
とりあえずはインプット量を増やすことを心がけました。

結果、ミッション達成のためにこちらから提案したり、
エンジニア組織についての思いをメンバーにアウトプットすることが増えたと感じます。

 

自身の成長が一番ではなく、
組織としての成功を一番として行動できるような「マインドの変化」が自分の中であったことが、行動の変化に結びついているのかもしれません。

 【振り返り2】 本を読むようになったから?

マネジメント能力を強化したく、色々本を読むようになりました。

  • 効率的な学び方
  • コーチン
  • エンジニア組織
  • リーダーシップ
  • ビジネス本
  • 経理
  • 心理学

などの本を読んでいて
「あの本に書いてあった内容とこの本のこの部分って似てるなー」
とか
「あの本で言ってたことってこういうことだったのか!」
と他の本を読んでいるときに気づくことがよくありました。

全く違う分野の本なのにこういう事が頻繁にあり、
「学ぶ」ことに境界線を作っていた自分が恥ずかしいと思うようになりました。

 

このように勉強する意識が変わり学びの視野が広がったことで、
様々な分野への興味に結びついているのかもしれません。

 

【振り返り3】そもそもコミュニケーション機会が増えたから?

EMになると様々な部署と連携した課題解決を行うようになるため、
今まで関わらなかったビジネスサイドの人やバックオフィスの人と関わりが増えました。
また1on1で多くのメンバーと話す機会もあり、
基本的に誰かとコミュニケーションを取ることが仕事になってきています。

様々な人とコミュニケーションを取るなか、
「ビジネスサイドの人の説明の納得感や語彙力すごいなー」
「この人の説明わかりやすいから今度真似してみよう」
「メンバーの話し方や表情から色々情報が取れるの大事だしおもしろい」
と様々な学びを得たり、コミュニケーションの重要性に気づいたというのが大きいです。

今までは自分がコミュニケーション苦手だからと、
積極的にコミュニケーションを取ってきませんでしたが、
「コミュニケーションを取っていないからこそコミュニケーションが苦手だった」ということに気づいたのです。

 

今はコミュニケーションに対するネガティブな印象は無くなり、
話すことで得られる学びの好奇心の方が強くなったため、
積極的な行動に変化しているのだと感じます。

最後に

一度マネジメントを経験することで目線が変わり、
今まで見えなかったもの・見ようとしなかったものが見えてくるようになることで「考え方=思考」が変化していきました。

マネジメントを「経験」することで「思考」が変化し、
「思考」が変化することでそれはやがて「行動」へと変化していきます。
その結果が今の私を作っているのではないかと今回振り返ってみて感じました。

これらの経験はマネジメントだけでなく、今後プレーヤーのときにも活かせることであると感じます。

 

EMになって学んだこと。
それは、「自分自身で壁や境界を作らず、何にでも興味を持ちインプットし思考し続けること」です。
雑食に何でも学ぶ姿勢が自身を成長させてくれるのかもしれません。

 
株式会社Wizでは雑食に学ぶエンジニアを募集しています!
↓↓↓興味ある方はぜひご覧ください!↓↓↓
careers.012grp.co.jp