やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

リモートワークでコミュニケーションが足りないなら、モブプロすればいいじゃない

リモートワークが急激に広まる中、その利点とともに、チームのコミュニケーションが不足しがちという不安も聞こえてきます。 その対策としての、リモートモブプログラミングのススメです。

普段の職場へのモブプログラミングの導入には色々と障壁もありますが*1、急なリモートシフトの混乱に乗じてを円滑に進める施策のひとつとして試してみる価値はあるのではないでしょうか。

モブプログラミングとは

まず、モブプログラミング(以下モブプロ)について。知ってる方は飛ばしてください。

モブプロとは、プログラミングを

  • 同じ仕事を
  • 同じ時間に
  • 同じ場所で
  • 同じコンピューターで

行なうことです。

コンピューターを操作するタイピスト*2は交代制で、その場の全員で目の前の問題解決に取り組みます。 ナビゲーターは決してオブザーバーや傍観者ではなく、それぞれ考え、検索し、話し合い、決定します。むしろタイピストは入力するだけなので、言語やドメイン知識があまりないメンバーでもこなせます。

なお、モブ「プログラミング」と銘打たれてはいますが、プログラミングに限らず様々な作業をモブで行なう事例も聞いています*3
また、普段プログラミングを主としていないテストエンジニアやプランナーがプログラマのモブに混ざって知識を共有しつつ作業を進める事例も聞きます。

より詳しく、また、うまく始めるためのコツなど、次のスライドをぜひ参照してください。

speakerdeck.com

実際にやってみて実感しているのは以下のような点です。

  • 暗黙知暗黙知のまま共有できる情報量
  • 知識は、必要なときにタイムリーにインプットするほうが吸収できる
  • プログラミングでは調査の時間がそれなりにあるが、それを手分けできる(もしくは誰かが知っている)ので意外と効率は悪くない
  • 探索的なコードリーディングでも、迷子になりにくい
  • 従来、PRレビューで指摘したりしなかったりすることを、ストレスなくコードに反映できる

リモートモププログラミング

モブプロの定義のうち「同じ場所で」をオンラインで実現するのがリモートモブプロです。

リモートで行なうメリットとして、オンサイトにおける以下の問題が解消します。

  • オフィスに場所がない(会議室が空いていない、自席でやると近所迷惑、大きいモニタが無い)
  • メンバー間でキーボードやエディタの差異があり「同じコンピューターで」コードを書くストレス

以下、自分の体験と、Remote Mob Programming | How we do Remote Mob Programming. および How Remote Mob Programming Is Working for Me を加味したおすすめのセットアップを紹介します。

画面共有

開発環境は参加者個々のものを使い、ビデオ会議システムの画面共有 (screen sharing) 機能でタイピストの画面を全員にシェアします。

いくつか試した範囲では、Zoom Proアカウントがあれば*4Zoomが最もおすすめできますが、Google HangoutsやDiscordでも代用可能です。

ポイントは、

  • 上に挙げたサービスでは、画面共有はビデオ会議セッションのオーナーに限らず任意の参加者が開始できます。ビデオ会議セッションは1つを維持したままで大丈夫です
  • ウィンドウ単位での共有でなく、ディスプレイ1つを指定して共有すべきです。ビルド・実行などでエディタとは別ウィンドウを使用する際に、その内容もモブに対してシェアするためです
  • VSCode Live Shareなど、IDEのコラボレーション機能は(現時点では)モブプロには不向きです。表示するエディタタブおよびスクロール位置を共有できないため、みんなバラバラの箇所を見てしまい、また各自好きに編集できるため、モブとしてディスカッションするのを放棄して個々のアイデアでコードを書いてしまいがち
  • Zoomのリモートコントロール (remote control) 機能を使って1台のコンピューターの操作権をタイピストに渡す方法もありますが、ラグがコーディングの妨げになるため推奨しません。ラグが気にならない作業でなら検討の余地はありそうです

また、モブプロでは参加者すべてが仕事に集中し、貢献することを求めます。特にリモートでは内職しないように、 Remote Mob Programming | How we do Remote Mob Programming. では常に全員のカメラをonにすることを推奨されています。
しかし、慣れているチームや少人数であれば、帯域を節約するためにもカメラoffでも問題ないでしょう。その代わり、マイクは全員常にミュートにせず、相づちは声に出すよう心がけます。

タイピストの交代契機

Remote Mob Programming | How we do Remote Mob Programming. では10分インターバルを提案されています。

慣れてくれば時間制でなく、キリの良いところやタスクボードを用意して短時間で終わるチケットに区切っていく方法でも良いのですが、はじめは時間制が安心です。 休憩タイミングの設定も「n巡したら休憩」と決めやすくなります*5

オンサイトと違いタイピスト交代のコストがかかりますので、いずれにせよ時間は長めに設定するのが良さそうです。

Gitリポジトリによる受け渡し

Remote Mob Programming | How we do Remote Mob Programming. では、タイピストの交代ごとにWIPコミットをGitのリモートリポジトリ(モブセッションのブランチ)にpushし、次のタイピストがpullして続きの作業を行なう方法が紹介されています。

また、その操作をすばやく行なうためのコマンドラインツール mob が公開されています。これはGo言語製のコマンドラインツールで、次のように使用します。

$ mob start 10

これでブランチmob-sessionが作られ、その中でコードを書いていきます。"10"は交代までの時間指定(分)で、通知とデフォルトではsayコマンドの音声で交代を教えてくれます(macOSの場合)。

時間が来たら、

$ mob next

で作業状態をコミット(コミットメッセージはmob next [ci-skip])、pushしてくれます。

次のタイピストは自分のコンソールで

$ mob start 10

を実行すると、ブランチmob-sessionをpullしてくれるので続きの作業ができます。これを繰り返して進めていきます。

一連の作業が終わったら、

$ mob done

でブランチの作業内容がスカッシュされます*6。 適切なメッセージを付けてコミット、pushすることで完了です。

なお、mobコマンドの利用にあたっては、いくつか注意点があります。

  • リモートブランチ名は環境変数で設定します。1つのリポジトリで複数モブが作業する場合、モブごとに設定しましょう
  • リモートブランチをupstreamに設定しておく必要があります
  • mob done 実行時点でリモートブランチが消えてしまうのでオペレーション注意
  • mob start コマンドに share オプションを追加することでZoomの画面共有を開始(切り替え)できますが*7、あらかじめ以下の設定が必要です
    • Zoom > 設定 > キーボードショートカットを開き、「画面共有の開始/停止」の「グローバルショートカットを有効化」チェックボックスをonにします
    • macOS Catalinaの場合、スクリプトからの操作に制限がかかっています。システム環境設定 > セキュリティとプライバシー > "プライバシー"タブ > "アクセシビリティ" を開き、「下のアプリケーションにコンピュータの制御を許可」に /usr/bin/osascript を追加します*8。ただし、悪意のあるスクリプトによる操作も受け入れてしまう設定のため注意が必要です (4/6追記)

mobコマンドを使わない場合でも、交代をスムーズに行なうため「交代時のコミットメッセージにはこだわらない」「後でスカッシュする」等、ルール化しておくとよいでしょう。

[5/18追記] mobコマンドをIntelliJ pluginに移植したものをリリースしました。IntelliJ IDEsユーザの方はぜひ使ってみてください! plugins.jetbrains.com

マイク

環境音やエコーを防ぐため、外付けのマイクの使用を推奨します。指向性のあるコンデンサマイク(数千円のもので十分)で自分の声のみ拾うようにするだけで、ハウリングやタイプ音がなくなります。

私が使っているのはこれ

もしくは、スピーカーでなくヘッドホンを使用するだけでもハウリングは防げます。 ノイズフィルタリングしてくれるソフトウェアもありますが*9、音声品質はストレスの無いモブプロのために重要ですので、ハードウェアで何らかの対策をしておくことをおすすめします。

ホワイトボード

設計についてディスカッションする際は、オンラインで共有できるホワイトボードがあると便利です。 我々もまだ選定中なのですが、Miroは評判もよいですし、タスクボードやテキスト入力もできてこれひとつあれば大丈夫そうな感触です。

miro.com

描くだけであれば、ZoomやMicrosoft Teamsのホワイトボード機能や、Google JamboardやMagicalDrawといった選択肢もあります。 プログラマーは、ペンタブレットや液晶タブレットよりiPadAndroidタブレット端末の所有率が高いと思いますので、iOS/Android対応しているサービスがよいでしょう。

gsuite.google.co.jp

draw.kuku.lu

タスクボードは、GitHub ProjectsやGitKraken Glo Boardsなどの選択肢もあります。

GitKrakenの利用はぜひこちらから! www.gitkraken.com

まとめ

もちろんモブプロは万能ではなく、分担作業が向いているものもたくさんあります。しかし、全くやらないのはもったいないことだと思うので、今この状況を好機だと思って、試してみてはいかがでしょうか。

モブプロ自体、チームによって様々なやり方がありますが、最初は少々やりにくさを感じても今回紹介したようなテンプレートに沿ってやってみることをおすすめします。

参考資料

speakerdeck.com

speakerdeck.com

takaking22.com

(4/4追記) piyolog.hatenadiary.jp

*1:上司の説得(生産性が下がるのではないか等)、会議室が空いていない、自席でやると近所迷惑、大きいモニタが無い、キーボードやエディタの差異など

*2:ペアプロにおける「ドライバー」ですが、モブでは「タイピスト」のほうがふさわしいと思うのでこの表現で

*3:「モブワーク」「モブデザイン」「モブテスティング」などで検索してみてください。ただし万能ではなく、作業によって向き不向きはあります

*4:Proアカウント以上でないと40分制限があります

*5:意識的に休憩を入れないと、つい連続でやってしまって疲労困憊します(しました)

*6:この時点でリモートブランチも削除されますので注意!

*7:macOSLinuxのみ

*8:あらかじめFinderの 移動 > フォルダへ移動 で /usr/bin に移動し、サイドバーにドラッグ&ドロップして追加しておくことで指定可能になります

*9:https://krisp.ai/ など

UWA GOTでUnityアプリのプロファイリング(iOS編)

Unity向けプロファイリングツールであるUWA GOTのv2.0.1から、iOSプラットフォームでのプロファイリングも可能になりました*1。 本記事では、iOS版の導入手順(Androidとの差分)と注意点などを紹介します。

なお、UWA GOTの概要については、過去記事を参照してください。

www.nowsprinting.com

www.nowsprinting.com

SDKインテグレーション

基本的なインテグレーションはAndroidと同様です。 ただし、最初に開くSceneのヒエラルキには、UWA/Prefabs/UWA_Launcherを追加します*2

以上設定したら、Development Buildを有効にしてビルドします(Xcodeプロジェクトを生成します)。

Bitcodeを無効化

XcodeプロジェクトはデフォルトでBitcodeが有効な設定になっていますが、UWA GOTのiOSライブラリはBitcodeに対応していません*3。 そのため、Build Settings > Project*4 > Build Options にある"Enable Bitcode"を探して"NO"に設定する必要があります(下図参照)。

f:id:nowsprinting:20200222224220p:plain

なお、ビルドごとに毎回手作業で修正するのはつらいので、Unityビルドの後処理でXcodeプロジェクトの"Enable Bitcode"を"NO"に書き換えるスクリプトを入れておくと便利です。

以下のコードをEditorディレクトリ下に配置します。このコードは、Unity 2019.3.2f1 + Xcode 11.1で動作を確認しています。

public class DisableBitcode : IPostprocessBuildWithReport
{
    public int callbackOrder => 0;

    public void OnPostprocessBuild(BuildReport report)
    {
        if (report.summary.platform == BuildTarget.iOS)
        {
            var path = report.summary.outputPath;
            var pbxProjectPath = PBXProject.GetPBXProjectPath(path);
            var replaceProject = new PBXProject();
            replaceProject.ReadFromString(File.ReadAllText(pbxProjectPath));

            var target = replaceProject.GetUnityFrameworkTargetGuid();
            replaceProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO");

            File.WriteAllText(pbxProjectPath, replaceProject.WriteToString());
        }
    }
}

スクリプトは、下記ブログ記事を参考にさせていただきました*5

blog.naichilab.com

計測

UWA GOTをバンドルしたアプリを起動すると、下図のGUIがオーバーレイされます。

f:id:nowsprinting:20200222224757p:plain

"Direct Mode"は、onにするとアプリが終了し、再起動するところから計測を開始できます。offのときは、"Overview", "Assets"いずれかをタップしたタイミングで計測を開始します。

計測が開始されると"Stop"ボタンが表示されるので、一通り動作させたらこれをタップして計測を終了します。 一度計測を行ったら、アプリを再起動しなければ次の(他の)計測はできません*6

f:id:nowsprinting:20200222224804p:plain

計測データのアップロード

Android版と異なり、計測が終了するとデータをアップロードするためのダイアログが表示されます。

f:id:nowsprinting:20200222225228p:plain

UWAオンライン(IDとパスワード入力が必要)、もしくはローカルサーバ(IPアドレスの入力が必要*7)を選択し、[データ提出]をタップします。

Onlineの場合、スクリーンショットをUWAオンラインサーバに送信するかを指定するトグルが表示されます*8。[確認]をタップすると計測データが送信されます。

f:id:nowsprinting:20200222225236p:plain

計測データの表示

以降は、Android版と同様です(ただしMonoプロファイルはありません)。過去記事およびマニュアルを参照してください。

*1:本稿執筆時点ではv2.0.2がリリースされており、本稿はv2.0.2ベースで書いています

*2:v2.0.1まではプラットフォームごとに別ファイルでしたが、v2.0.2から統一されました

*3:UWAに修正要望を出しています

*4:ProjectでなくUnityFrameworkだけでよさそうですが、スクショ撮りなおすのが面倒だったのでここままで…

*5:ただしUnity 2019.3でPBXProjectの構成が変わっていたため修正

*6:改善要望はしており、修正予定はあるそうです

*7:Unityエディタ上でGOT Editorを開き、WIFIボタンをクリックすると起動します。IPアドレスとポート番号が表示されますがポート番号は入力不要です

*8:Direct Modeもそうなのですが、トグルon/offがとても視認しづらいです。要望は上げています…

UWA GOT v2.0.2でAndroidのIL2CPP・ARM64に対応された模様

Unity向けプロファイリングツールであるUWA GOTのv2.0.2がリリースされていました*1。 追加機能として書かれているのはAndroidおよびWindowsプラットフォームでのIL2CPPビルドとAndroidプラットフォームでのARM64対応ですが、細かい使い勝手の向上も入っていたので、あわせて紹介します。

UWA GOTの導入については、過去記事を参照してください。

www.nowsprinting.com

www.nowsprinting.com

AndroidのIL2CPP・ARM64に対応

これまで、AndroidプラットフォームでUWA GOTを有効にするには、スクリプティングバックエンドにMonoを選択する必要がありました。 v2.0.2からは、IL2CPPを選択してビルドしてもUWA GOTが有効になります。また、ターゲットアーキテクチャにARM64を選択しても同様に有効になります。

ただし、IL2CPPビルドではプロファイリング項目の"Mono"テストモードで採取できません。 Monoテストモードでは、ヒープを多く使用しているメソッドやメモリリークの疑いのある関数を探すことができます。こちらを利用したい場合には、スクリプティングバックエンドをMonoに変更してビルドする必要があります。

複数プラットフォームの切り替えが楽になった

v2.0.1までは、以下の理由からプラットフォーム切り替えごとにUWA GOTのSDKをプロジェクトから削除してインポートしなおすことが必要でした。

  • 最初のSceneに配置するprefabのファイルがプラットフォームによって異なる
  • dllの中に同名のシンボルが存在する

これがv2.0.2では解消し、あらかじめ複数のプラットフォーム向けSDKをインポートしておいて、以降は普段通りSwitch Platformで切り替えることができます。

スクリーンショットのアップロードを抑止できるようになった

プロファイリングに必要な各種データとともにスクリーンショットを確認できるのはUWA GOTの良いところなのですが、IPモノを扱っている場合は逆に使いにくいという話も聞きます。

v2.0.2では、採取した計測データをオンラインサービスにアップロードするタイミングで、これを抑止する選択肢が追加されました。

下図は、AndroidのUWA GOT Appで計測データをアップロードする画面です。「スクリーンショットをGOT Onlineにアップロードします」のチェックをoffにすることで、オンラインサービスにはスクリーンショットなしのデータが送られます*2

f:id:nowsprinting:20200221092627p:plain

なお、このチェックボックスが機能するのはオンライン向けのアップロードのみであり、Unityエディタ上で動作するローカルサーバへのアップロードは強制的にスクリーンショットありで送られます。

ローカルサーバに送られた計測データは、そこからさらにオンラインにアップロードすることができます。このとき、下図のようにスクリーンショットの送信有無を指定できます*3

f:id:nowsprinting:20200221094228p:plain

まとめ

今回のアップデートでかなり使い勝手が改善された感じです。日本でももっと流行るといいなと思っています。

*1:日付は2019/12/27。なんのアナウンスもなかったので気づきませんでした

*2:ただし、パケットキャプチャして確認したわけではないので保証の限りではありません

*3:これもパケットキャプチャして確認したわけではありません

JetBrains ReSharper Command Line Tools 2019.3でコードインスペクションを実行する

JetBrains製のC# IDEであるRiderにあるコードインスペクション機能を、そのままコマンドラインで実行できるツールがReSharper Command Line Toolsとして提供されています。 ReSharper Command Line Toolsは、これまでWindows版しかサポートされていませんでしたが、2019.3からはmacOS及びLinuxでも動作するようになりました。

www.jetbrains.com

ReSharper Command Line Toolsには、コードインスペクションツールであるInspectCodeのほかにも、重複コードを検出するdupFinder、コードクリーンナップを行なうCleanupCodeが含まれています。 今回はInspectCodeのみ紹介します。

ReSharper Command Line Toolsのインストール

ReSharper Command Line Toolsは、上記サイトからプラットフォームを選んでダウンロードできます。無料で、ライセンスキーも不要です。

ダウンロードしたzipファイルは、任意のディレクトリに展開するだけでokです。ターミナルで直接コマンド実行するのであればPATHを通しておくのも良いでしょう*1

ツールの実行にUnityエディタは必要ありませんが、ソリューションファイル (.sln) およびプロジェクトファイル (.csproj) が必要です。CIサーバで実行するときにはあらかじめUnityエディタで生成しておく必要があります*2

InspectCodeの実行

InspectCodeは、以下のように実行できます。Windowsの場合、inspectcode.shinspectcode.exeに置き換えてください。

$ inspectcode.sh \
    $(RESHARPER_TARGET) \
    --output=$(LOG_DIR)/inspect.xml \
    --format=xml

$(RESHARPER_TARGET)には、ソリューションファイル (.sln) のパスを指定します。

出力されるxmlファイルは、例えばJenkinsのPMD Pluginなどで表示できます。また引数に--format=htmlと指定することでhtml形式でも出力できます。

インスペクション内容の設定

InspectCodeがインスペクションしてくれる項目およびデフォルト設定は、下記ページにまとめられています。

Code Inspections in C# - Help | JetBrains Rider

設定をプロジェクトに合わせてカスタマイズするには、IDE(RiderおよびReSharper)のPreferencesの設定内容をファイル出力し、プロジェクトのルートに置くか、--profile引数でパスを渡します。

ファイル出力には2種類の方法があります。

設定をteam-sharedとして保存する

Preferencesを変更するとき、"Save"ボタンではなく、その横のプルダウンで"Solution team-shared"を選択します。

f:id:nowsprinting:20200114035826p:plain

すると、プロジェクトのルートに "プロジェクト名.sln.DotSettings" として保存されます。このファイルをリポジトリに追加すれば、CIサーバでも同じ設定でインスペクションを実行できます。

コンピュータの設定をエクスポートする

すでに設定変更を"Save"ボタンで保存していた場合、それはプロジェクトでなくコンピュータ単位の設定ファイルに保存されています。これをエクスポートし、team-shared設定ファイルとして利用できます。

コンピュータ単位の設定をエクスポートするには、Preferencesウィンドウ左下の"Manage Layers"ボタンでPreferences Layersウィンドウを開きます。

f:id:nowsprinting:20200114035841p:plain

"This computer"行を選択し、ツールバーにある"Export to File"アイコンをクリック、Export To Fileウィンドウを開きます。

f:id:nowsprinting:20200114035847p:plain

ここでエクスポートする設定を選択できます。今回はコードインスペクション向けなので"CodeInspection"だけ選択していますが、他の設定もチームで共有したい場合はここで選択し、"OK"ボタンをクリックします。

続いてファイル指定ダイアログが表示されますので、プロジェクトのルートに "プロジェクト名.sln.DotSettings" として保存します。

プラグインの追加

InspectCodeには、.nupkg形式のプラグインを追加することができます。追加は、.nupkgファイルをReSharper Command Line Toolsを展開したディレクトリに入れるだけで完了します。

例えば、先日の記事で紹介した、コードの複雑度を測るプラグインも公開されています*3

コグニティブ複雑度

plugins.jetbrains.com

サイクロマティック複雑度

こちらはReSharper 2019.3.1で正しく動作しないようです。特にエラーも吐かれていないので設定まわりだと思うのですが、詳細未確認。

plugins.jetbrains.com

まとめ

インスペクションツールの実行はCIに頼るのでなく、IDEに備わっていることで問題の作り込みを早期に防ぐことができます。 その点、RiderやReSharperを使い、かつ警告にきちんと対処していれば、内部品質の高い状態を維持できるはずです。

しかしながら、すでにプロジェクトの内部品質が低くなっている状態から改善していく場合など、CIでメトリクスを採取して変化を監視するのは有効です。ぜひ導入を検討してみてください。

なお、ReSharper Command Line Toolsのマルチプラットフォーム対応については、昨年9月のJetBrains .NET Meetup TokyoおよびCEDEC 2019のJetBrainsブースで「2020.x以降になるだろう」と伺っていたのですが、2019.3に前倒されました。たいへんありがたい。

関連記事

www.nowsprinting.com

関連書籍

*1:CIサーバで実行することになりますので、PATHには頼らずにMakefile等にパス込みで書くことをおすすめします

*2:通常、.gitignoreでトラッキング対象外にしているはずなので

*3:Rider向けではなく、ReSharper向けのプラグインである必要があります

JetBrains Riderでコードの複雑度を計測する

JetBrains製のC# IDEであるRiderには、様々なプラグインが提供されています。 その中から、コードの複雑度を計測するプラグインを2つ紹介します。

www.jetbrains.com

サイクロマティック複雑度

サイクロマティック複雑度(cyclomatic complexity)は循環的複雑度とも呼ばれ、コードの複雑さを測る指標としてよく使われます。 メソッド単位に分岐によるパスの最大数を算出した数値で、一般に、32以上でバグ混入率が高い、50以上でテスト困難と言われています。

循環的複雑度 - Wikipedia

これをRiderのインスペクションに組み込むプラグインがCyclomaticComplexityです。

plugins.jetbrains.com

CyclomaticComplexityプラグインは、一般のプラグインと同様、Preferences... > Plugins からインストール・有効化できます。 有効化すると、エディタのメソッド定義の上にサイクロマティック複雑度が表示されるようになります。

f:id:nowsprinting:20200112202350p:plain

上図では、サイクロマティック複雑度が29、Wariningとなるしきい値20(デフォルト値)に対して145%であると表示されています。

しきい値は、Preferences > Languages & Frameworks > Cyclomatic Complexity で変更できます。 既存プロジェクトで数値の高いメソッドが多い場合は、しきい値を緩めの値にして目立つものから少しづつリファクタリングを進めていくことをおすすめします。

f:id:nowsprinting:20200112202402p:plain

コグニティブ複雑度

コグニティブ複雑度(cognitive complexity)とは、2016年からSonarSource社*1が提唱している比較的新しい複雑度の指標です。循環的複雑度にならって日本語化するのであれば「認知的複雑度」でしょうか。

Cognitive Complexity - A new way of measuring understandability

SonarSource社は、サイクロマティック複雑度では保守性を正しく数値化できておらず、その点を改善したものがコグニティブ複雑度であると書いています。

例えば、サイクロマティック複雑度ではネストしたループとswitch-caseは同じスコアになりますが、後者のほうが理解しやすいコードのはずです。 また、異常系の処理をif文で深くネストした中に書くよりも、メソッドの先頭でエラーチェックしてreturnしてしまうほうが読みやすくなります。

そのため、分岐ではなく処理の流れを阻害する個所を加点することで保守性・可読性の評価につなげようというものがコグニティブ複雑度です。

このコグニティブ複雑度をRiderのインスペクションに組み込むプラグインがCognitiveComplexityです。

plugins.jetbrains.com

CognitiveComplexityプラグインは、一般のプラグインと同様、Preferences... > Plugins からインストール・有効化できます。 有効化すると、エディタのメソッド定義の上にコグニティブ複雑度が表示されるようになります。

f:id:nowsprinting:20200112202240p:plain

上図では、コグニティブ複雑度が23、Wariningとなるしきい値10(デフォルト値)に対して230%であると表示されています。またクリックすることでコード内の加点された個所(上図ではif文の横にある"+1")を表示できます。

しきい値は、Preferences > Languages & Frameworks > Cognitive Complexity で変更できます。 既存プロジェクトで数値の高いメソッドが多い場合は、しきい値を緩めの値にして少しづつリファクタリングを進めていくことをおすすめします。

f:id:nowsprinting:20200112202303p:plain

プロジェクト単位のインスペクション実行

いずれのプラグインもRiderのインスペクション機能に組み込まれていますので、Code > Inspect Code... でスコープを指定して実行できます。

f:id:nowsprinting:20200112214547p:plain:w300

実行結果は"Inspection Results"ウィンドウに表示されます。

f:id:nowsprinting:20200112202335p:plain

上図のように、WARNING > Potential Code Quality Issues の下に、サイクロマティック複雑度とコグニティブ複雑度それぞれ別に表示されます。

  • Element exceeds cyclomatic complexity
  • Element exceeds Cognitive Complexity threshold

なお、両方のしきい値を超えているメソッドは、コグニティブ複雑度にのみ表示されます。

まとめ

同じ目的を持つ2つのプラグインを紹介しましたが、いくつかのプロジェクトで見比べた感じ、コグニティブ複雑度のみ適用しておけばよさそうです。

複雑度の高いメソッドは、条件の組み合わせも多くなりテストすることが難しく、バグも作り込まれやすくなります。 プラグインを入れて数値を気にすることで、適切にメソッドを分割して保守性の高い状態をキープできるようになるでしょう。

なお、CyclomaticComplexityプラグインの存在はUnityゲーム開発者ギルドのSlackで知りました。色々と役立つ情報を交換ができるコミュニティですので、Unityでゲームを開発している方は入ってみてはいかがでしょうか。

scrapbox.io

関連記事

www.nowsprinting.com

www.nowsprinting.com

関連書籍

IntelliJ IDEA パーフェクトガイド

IntelliJ IDEA パーフェクトガイド

仮面ライダーゼロワン 変身ベルト DXザイアサウザンドライバー

仮面ライダーゼロワン 変身ベルト DXザイアサウザンドライバー

  • 発売日: 2019/12/28
  • メディア: おもちゃ&ホビー

*1:SonarQubeの開発・運営会社

JetBrains Rider 2019.3でUnityテストのカバレッジを採取する

JetBrains製のC# IDEであるRiderには、テスト実行時にステートメントカバレッジを採取するdotCoverプラグインが提供されています。

これまで、dotCoverプラグインはUnity Test Framework*1に対応していませんでしたが、2019.3からEdit Mode tests実行時のカバレッジを採取・表示できるようになりました。

www.jetbrains.com

dotCoverプラグインの有効化

dotCoverプラグインは、一般のプラグインと同様、Preferences... > Plugins から有効化できます。 ただし使用するには、"ReSharper Ultimate + Rider" または "All Products Pack" のライセンスが必要です(30日間の試用あり)。

また、計測対象のUnityプロジェクトには、JetBrains Rider Editorパッケージの1.2.0以降が必要です。バージョンが古い場合は、UnityエディタのPackage Managerでアップデートしてください。

本稿では以下のバージョンで動作を確認しています。

  • JetBrains Rider 2019.3.1
  • Unity 2019.3.0f1
  • JetBrains Rider Editor 1.2.1
  • Test Framework 1.1.9

カバレッジ採取の準備

dotCoverプラグインカバレッジを採取するには、以下の準備が必要です。

1. Unityエディタを "Start Unity with Coverage" で起動する

RiderでUnityテストを実行するにはUnityエディタとのコネクトが必要ですが、dotCoverを使用するときには通常とは異なる方法でコネクトします。 Unityエディタは一旦閉じ、RiderのUnityツールバー左端にあるUnityロゴをクリックして "Start Unity with Coverage" で起動します。

f:id:nowsprinting:20200111201900p:plain

もしほかの方法で起動したUnityエディタとコネクトした状態でカバレッジを採取しようとすると、下図のアラートが表示されます*2

f:id:nowsprinting:20200111201903p:plain

2. 実行環境にEdit Modeを選択する

カバレッジの採取は、"Unit Tests"タブにあるプルダウンで"Unity Editor - Edit Mode"を選択した状態で実行する必要があります。

f:id:nowsprinting:20200111204832p:plain

つまり必然的に、Play Mode testsのカバレッジは採取できません。

カバレッジの採取

カバレッジは、以下の手順でテスト実行することで採取できます。

  1. Riderのエディタでテストコードを開き、テストメソッド定義にカーソルを置いて alt + enter*2 でContext Actionsメニューを表示します
  2. すると下図のように "Cover" が選択できますので、enterを押下すると当該メソッドが実行されます

f:id:nowsprinting:20200111202202p:plain

もしくは、Rider下部の"Unit Tests"ウィンドウにある"Explorer"タブでEdit Modeテストのアセンブリを選択し、左にある"Cover"アイコンをクリックすると、すべてのEdit Modeテストを実行できます*3

f:id:nowsprinting:20200111215057p:plain

Coverを実行すると、テスト実行結果が"Unit Tests"ウィンドウに表示されるほか、"Unit Tests Coverage"ウィンドウにステートメントカバレッジが表示されます。

カバレッジの見かた

"Unit Tests Coverage"ウィンドウでは、テスト対象のステートメント数と、テスト実行によって通過した/していないステートメント数、またその割合が確認できます*4

f:id:nowsprinting:20200111201855p:plain

また、この状態でテスト対象ソースコードをエディタタブで開くと、通過した/していないステートメントを確認できます。 グリーンでマークされた行(下図16〜19, 25行目)は通過したステートメント、グレーでマークされた行(同22〜23行目)は通過していないステートメントです。

f:id:nowsprinting:20200111201857p:plain

ステートメントカバレッジの使いどころ

ステートメントカバレッジとは、ユニットテストによってテスト対象コードの全ステートメントのうち何%がテストされているかを示すメトリクスです。命令網羅、C0とも呼ばれます。

カバレッジ」には、ほかに、デシジョンカバレッジ、条件カバレッジなどが使われます。 上の例では19行目が通過していることしか確認できませんが、デシジョンカバレジを満たすには a == 0 のテストケースと、a != 0 かつ b == 0 のテストケースが必要です。

また、ステートメントが実行されたことは確認できますが、それが正しい実行結果であるかはアサーションで適切に判定されている必要があります。

以上の観点から、ステートメントカバレッジは品質の指標として信頼度の高いものとは言えず、あくまで「めやす」くらいにとどめるのが良いでしょう。

関連記事

www.nowsprinting.com

www.nowsprinting.com

関連書籍

IntelliJ IDEA パーフェクトガイド

IntelliJ IDEA パーフェクトガイド

  • 作者:横田 一輝
  • 発売日: 2019/11/27
  • メディア: 単行本(ソフトカバー)

仮面ライダーゼロワン 変身ベルト DXエイムズショットライザー

仮面ライダーゼロワン 変身ベルト DXエイムズショットライザー

  • 発売日: 2019/08/31
  • メディア: おもちゃ&ホビー

*1:Unity 2019.2以降パッケージ化された名称。詳しくは https://www.nowsprinting.com/entry/2019/09/28/132800 を参照

*2:Rider 2019.3.1から表示されるようになった模様

*3:Play Modeテストが存在しないのであれば、Tests > Cover All Tests from Solution でもすべてのテストを実行できます

*4:おそらくRider 2020以降で、ウィンドウに表示する(集計する)対象をフィルタリングできるようになります

JetBrains Rider 2019.3でUnityのPlay Mode testsを実行する

JetBrains製のC# IDEであるRiderの2019.3がリリースされました。 これまで、RiderからはUnity Test Framework*1のEdit Mode testsのみ直接実行することができましたが、2019.3からはPlay Mode testsも実行できるようになりました。

www.jetbrains.com

Unityテストの実行方法

基本的には、Edit ModeもPlay Modeも以下の手順で実行できます。

  1. Riderのエディタでテストコードを開き、テストメソッド定義にカーソルを置いて alt + enter*2 でContext Actionsメニューを表示します
  2. すると下図のように "Run" が選択できますので、enterを押下すると当該メソッドが実行されます

f:id:nowsprinting:20200110230813p:plain:w200

テスト実行結果は、Rider下部の"Unit Tests"ウィンドウに表示されます。

Unityテスト実行の前提条件

テストの実行はUnityエディタで行われるため、UnityエディタのプロセスとRiderとがコネクトされてる必要があります。

Unityエディタとのコネクトは、Rider右下に表示されているアイコンで判断できます。下図、右から3つめのUnityロゴ右下にグリーンの丸が表示されていればコネクトされています。

f:id:nowsprinting:20200110233055p:plain

UnityエディタからRiderを開いた場合はコネクトされた状態のはずです。Unityエディタを再起動したなどコネクトが切れた場合、Unityツールバーの左端にあるUnityロゴをクリックし、"Start Unity"で起動しなおすか、すでに起動しているプロセスに"Attach to Unity Process..."でコネクトできます。

f:id:nowsprinting:20200110233103p:plain

Play Mode testsの実行方法

Play Mode testsも、上記と同じ手順でテスト実行することができます。 ただし、"Unit Tests"ウィンドウにあるプルダウンで"Unity Editor - Play Mode"を選択した状態で実行する必要があります。

f:id:nowsprinting:20200110230824p:plain:w500

これを切り替えずに実行すると、上図のように"Inconclusive: Test not run"と表示され、テスト実行されませんので注意してください。

なお、逆に、Edit Mode testsを"Unity Editor - Play Mode"で実行しようとしても同様にテスト実行できません。"Unity Editor - Edit Mode"を選択した状態で実行する必要があります。

所感

Riderから直接Play Mode testsが実行できるようになったことは喜ばしいことです。 しかし、プルダウンでの切り替えが必要ですし、Play Mode testsの最大のメリットであるプレイヤー(iOSAndroidの実機など)での実行ができるわけではありません。 個人的には、引き続きEdit Mode testsを中心に書いていくのがよいと考えています。

Tips

デバッグ実行

Context Actionsメニューに表示される "Debug" を選択すると、テストをデバッグ実行することができます。 もちろん、ブレイクポイント、ステップ実行、変数のインスペクションなども使用できます。

アセットの自動リフレッシュを抑止する

Riderのデフォルト設定では、テスト実行の都度、Unityエディタでアセットのリフレッシュが実行されます。 特にTDD (Test Driven Development: テスト駆動開発) で開発を進める場合など、頻繁に待ち時間が発生してしまいますので、この設定をオフにすることもできます。

Preferences... > Languages & Frameworks > Unity Engine を開き、"Automatically refresh assets in Unity" チェックボックスをoffにします。

www.jetbrains.com

関連記事

www.nowsprinting.com

www.nowsprinting.com

関連書籍

IntelliJ IDEA パーフェクトガイド

IntelliJ IDEA パーフェクトガイド

*1:Unity 2019.2以降パッケージ化された名称。詳しくは https://www.nowsprinting.com/entry/2019/09/28/132800 を参照

*2:Mac OS X 10.5+ Keymapの場合