やらなイカ?

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

Unity Test Framework learning materialsをやってみた

Unity Test Frameworkパッケージのバージョン1.3では、マニュアルの中にUnity Test Framework learning materialsというドキュメントが追加され、またそのサンプルコードがパッケージのSamplesに含まれるようになりました。

ドキュメントは、Unity Test Frameworkの基本的な使いかたを紹介するGeneral Introductionと、サンプルプロジェクトを題材にゲームの操作を伴うテストを書くTesting Lost Cryptで構成されています。

以下、それぞれ紹介します。

General Introduction

Unity Test Frameworkの基本的な使いかた(ユニットテスト中心)を紹介しています。

サンプルコードはUnity Test Framework v1.3.0のSamplesにあるのですが、恐らくパッケージの設定に不備がありPackage Managerウィンドウからインポートすることができません*1 *2。 サンプルコードを見たいのであれば、Unity Test Frameworkをv1.3.0にアップグレードし、 Library/PackageCache/com.unity.test-framework@1.3.0/Samples~/ にあるファイルを直接見るしかなさそうです。

サンプルは、例えば1_RunningTest_ProjectがExercise1のテスト対象のみ、模範解答的テストを書いたものが1_RunningTest_Project_Solutionにあるという形式です。 なお、Exercise間で同じ名前のアセンブリを定義しているため、そのままでは同時に1つのSampleしかインポートできません*3。 インポートしたらasmdefファイル内のアセンブリ名を変えていくとよいでしょう。

以下、各Exerciseの内容です。

  1. Running Tests: 最初のテストを書いて実行するもの。テスト対象にバグがあって修正するというものなので、テスト初心者の方は1_RunningTest_Projectから見ることをおすすめします
  2. Arrange, Act, Assert: 読みやすいテストの書きかたとして3Aを紹介*4
  3. Semantic Test Assertion: NUnit3の制約モデルでのアサーションの書きかた、Does.Contain().And.Contain()といった表現力を紹介
  4. Custom Comparison: Unity Test FrameworkのEqualityComparerで許容誤差ありの比較
  5. Asserting Logs: LogAssert.Expectによるログ出力の検証
  6. Set Up and Tear Down: テストメソッドの前後に処理を行なう方法*5
  7. PlayMode Tests: 再生モードでテスト実行する方法
  8. PlayMode Tests in Player: スタンドアロンプレイヤーでPlay Modeテストを実行する方法
  9. UnityTest Attribute: UnityTest属性による複数フレームにまたがるテストの例*6
  10. Long Running Tests: 実行に時間がかかるテストにCategory属性およびExplicit属性を指定して実行タイミングをコントロールする方法
  11. Scene Based Tests: Edit ModeテストでSceneファイルをロードしてScene内のGameObjectを検証する方法*7
  12. Build Setup and Cleanup: テストのビルド前後に処理をはさむ IPrebuildSetup および IPostBuildCleanupの使いかた。テスト対象のSceneがプロジェクトのScenes in Buildに無いときに追加している*8
  13. Domain Reload: Edit Modeテストでスクリプトコンパイルなどドメインリロードを挟むテストの書きかた*9
  14. Preserve Test State: ドメインリロードが発生するテストでは変数が失われるので、回避策としてSerializeField属性を使う例*10
  15. Test Cases: TestCase属性によるパラメタライズドテストの書きかた。本文ではTestCaseSource属性・Values属性・ValueSource属性にも触れられています
  16. Custom Attributes: NUnitのカスタム属性を定義する例*11
  17. Runnings Tests Programmatically: TestRunnerApiの使用例。Sceneのバリデーションをテストとして実装し、それをメニューから呼ぶサンプル

Exercise16を除けば拙書『Unity Test Framework完全攻略ガイド』でも解説している内容です。日本語でより詳しい解説がほしい方はぜひ購入を検討してください。

www.nowsprinting.com

Testing Lost Crypt

2Dゲームサンプル『Lost Crypt』を題材とした統合テスト(ユニットテストより結合度の高いテスト)の書きかたです。 こちらはSamplesにファイルはありません。

以下、内容と補足説明です。

Setting up

題材となるLost Cryptプロジェクト(無料)のセットアップを行います。

assetstore.unity.com

Running a test in LostCrypt

Sceneをロードして、GameObject "FX - Day"が存在することを確認するテストを追加します。

Moving character

テストコードからキャラクターを操作する仕組みを実装します(まだテストではない)。

Lost CryptはNew Input Systemパッケージ(UnityEngine.InputSystem名前空間)を使用していますが、New Input SystemのInputAction(抽象化層)は使用しておらず、低レベルAPIを使用して独自の抽象化層(CharacterController2Dクラス)を実装しています。 そのため、テストの実装方式は旧Input Managerでも近い形式で実装できるものになっています。

New Input SystemのInputActionを使用しているのであれば、New Input SystemパッケージのマニュアルにあるInput testingが参考になるでしょう*12

Reach wand test

キャラクター操作の延長で、Scene右端にあるワンドにたどり着くテスト。 たどり着くかはキャラクターの座標で判定、規定時間を過ぎたらタイムアウト*13で失敗するテストを書きます。

Collision test

コリジョン抜けのテスト。 実際にコリジョン抜けバグがあるのが草。

Asset change test

操作(ワンドまでたどり着く)によってアセットの変化(SpriteLibraryのnameが切り替わること)を検証するテスト。

Scene validation test

Scenes in Buildに指定されている各Sceneに対して、SaraとWandが存在することを検証するパラメタライズドテストの例。

Performance tests

Performance Testingパッケージを使用して、fpsを計測・検証する例。

まとめ

こういったコンテンツが公式から出てくることはとても良いですね!*14

これを機にUnityのテストについて詳しく知りたい! という方は、ぜひ拙著『Unity Test Framework完全攻略ガイド』を読んでみてください*15

www.nowsprinting.com

また、統合テストにはAutomated QAパッケージも有用です*16。こちらも拙著『Unity Automated QA攻略ガイド』を参考にしてください。

www.nowsprinting.com

*1:いくつかのUnityバージョンでダメでした。そうゆうとこやぞ…

*2:v1.3.2で修正されました。see: https://unity3d.atlassian.net/servicedesk/customer/portal/2/IN-23368

*3:ほんとそうゆうとこ…

*4:拙書『Unity Test Framework完全攻略ガイド』ではTeardownを加えた「4フェーズテスト」を紹介しています。ほぼ同じものです

*5:ファイル削除はTearDownでなくSetUpで行なうべきです。例として仕方ないところなのですが

*6:SamplesにはInitTestScene637147869846377210.unityというファイルが含まれているのですが、これはPlay Modeテスト実行時に自動生成されるSceneなので不要です

*7:これもTearDownでEditorSceneManager.NewSceneしているのはよくない。SetUpでやるべき

*8:拙書『Unity Test Framework完全攻略ガイド』ではITestPlayerBuildModifierで行なう方法を紹介しています

*9:SamplesのコードはパスのセパレータがWindowsのもの(\)になっています。ほかのOSの場合は修正要

*10:ドメインリロードでは(OneTimeSetUpやSetUpは再実行されるけど)UnitySetUpは再実行されないことが前提知識なのですが、書かれていない

*11:ただし例として書かれているものはMaxTime属性で実現できるものだし、使用しているIWrapTestMethodは非同期テストに使用できない属性なので、よい例とは思えない

*12:こちらもいくつかツッコミどころがあるのですが、それはまた稿を改めて

*13:Timeout属性に触れられていないのですがyield return nullではタムアウトが機能しなかったりテストがフリーズしたりします

*14:質はともかく、と言いたいところなのですが、品質関係のコンテンツの品質が低いのはちょっとよくないなと思うのです…

*15:Amazon Kindle版・ペーパーバック版も進めています。今しばらくお待ち下さい

*16:開発停止中ですが、Unity Test Framework v1.3で非同期テストも実装されたので、そろそろ開発再開されることを期待しています

Unity Test Framework v1.3で非同期テスト

Unity公式のユニットテストレームワークであるUnity Test Frameworkパッケージのバージョン1.3がリリースされました。

v1.2はスキップされ、v2.0での実装を予定されていた非同期テストのサポートが実装されています。

アップグレード方法

Package Managerウィンドウにv1.3が表示されれば*1そのままアップグレード可能です。 v1.3が表示されない場合、Packages/manifest.jsonファイルを直接開き、com.unity.test-frameworkのバージョンを1.3.0に書き換えます。

なお、前提UnityバージョンがUnity 2019.2から2019.4に上がっています。 依存パッケージであるcom.unity.ext.nunitも、v1.0.6からv2.0.3に上がっています*2

また以下の注意事項があります。現時点でのアップグレードは自己責任で。

  • 廃止されたAPIがあるため、例えばGraphics Test Frameworkコンパイルエラーを起こしています。これは意図的なものかどうか疑問で、今後修正される可能性もありそうです
  • Package Managerでエラーが出る(Unityバージョンによる?)。いくつかのバージョンで試しましたが、Samplesの表示が行われておらず、何らかの問題は出ていそうです*3

非同期テスト

これまでは非同期処理のテストはUnityTest属性をつけたコルーチンしかサポートされていませんでした。 v1.3からは、次のようにTest属性のテストとしてasyncメソッドを呼び出すことができます。

[Test]
public async Task 非同期テストの例_Taskをawaitできる()
{
    await Foo(1);
    var actual = await Bar(2);
    Assert.That(actual, Is.EqualTo(3));
}

private static async Task Foo(int id)
{
    await Task.Delay(200);
}

private static async Task<int> Bar(int id)
{
    await Task.Delay(200);
    return id + 1;
}

もちろんUniTaskも使用できます。

[Test]
public async Task 非同期テストの例_UniTaskをawaitできる()
{
    await UniTaskFoo(1);
    var actual = await UniTaskBar(2);
    Assert.That(actual, Is.EqualTo(3));
}

private static async UniTask UniTaskFoo(int id)
{
    await UniTask.Delay(200);
}

private static async UniTask<int> UniTaskBar(int id)
{
    await UniTask.Delay(200);
    return id + 1;
}

今回試したコードは、『Unity Test Framework完全攻略ガイド』サンプルプロジェクトにマージ済みです。 下記リンク先を参照してください。

github.com

また、SetUp/TearDown属性を付与したメソッドにもasyncキーワードを指定して非同期化することができます。 こちらもサンプルコードを参照してください。

github.com

github.com

[2023/1/29追記] 非同期テストでできること/ できないことを確認してまとめました

www.nowsprinting.com

参考

www.nowsprinting.com

www.nowsprinting.com

*1:されないこともあり謎。v1.3は実験的バージョンではないのですが…

*2:Unity Test Framework v2.0.1-exp.1ではv2.0.2でした

*3:v1.3.2で修正されました。see: https://unity3d.atlassian.net/servicedesk/customer/portal/2/IN-23368

PlatformIO for CLionで #M5Stack へのuploadポートを環境変数で指定する

M5Stack Basic v2.6に搭載されているシリアル変換ICであるCH9102FのmacOSドライバ(v1.7時点)では、M5Stackが接続されているシリアルポートを明示的に指定しないでuploadしようとすると次のエラーで失敗します。

(snip)
Looking for upload port...
Auto-detected: /dev/cu.usbmodemXXXXXXXXXXX
Uploading .pio/build/m5stack-fire/firmware.bin
(snip)
A fatal error occurred: Failed to write to target RAM (result was 01070000: Operation timed out)
*** [upload] Error 2

本来接続すべき /dev/cu.wch* でなく /dev/cu.usbmodem* に接続しようとして失敗しています。 この事象および、回避方法としてuploadポートをplatformio.iniファイルに指定する方法が下記ブログで紹介されていました(たいへん助かりました)。

blog.magcho.com

しかし、シリアルポートをリポジトリに書き込んだり、それをM5Stackをつなぎ替えるたびに書き換えるのも面倒なので、別の手段を探ってみました。

検証環境

検証環境は次のとおりです。

  • macOS 11.2.3 (Big Sur)
  • CLion 2022.2.3
  • PlatformIO for CLion plugin 22.3739.54
  • M5Stack Basic v2.6

PLATFORMIO_UPLOAD_PORT環境変数

uploadポートは、グローバル環境変数 PLATFORMIO_UPLOAD_PORT で指定することもできます*1

uploadだけCLIで実行するのであれば、次のように指定できます。

PLATFORMIO_UPLOAD_PORT=$(ls /dev/cu.wch*) pio run --target upload --environment m5stack-core-esp32

しかしこれではテスト実行やデバッグ実行を考えると不便です。 そこで、.bashrcに次のように追加することで、uploadもCLionのGUIから実行できるようにしました。

export PLATFORMIO_UPLOAD_PORT=$(ls /dev/cu.wch*)

.bashrcが読まれるのは(uploadなどの実行時でなく)CLion起動時なので、設定後CLionの再起動が必要です。 また、M5Stackをつなぎ替えたときにもCLionの再起動が必要な点にご注意ください。

コード全体はこちらを参照

github.com

Run Configurationで環境変数を指定できるとよいのでは?

とはいえ、PlatformIO for CLion pluginではPlatformIO Coreつまりpioコマンドを実行していますので、Run Configurationで環境変数を指定できると便利そうです。

これを要望としてIssue Trackerに登録しましたので、類似の悩みをお持ちの方はvoteもしくは、より良い提案をコメントいただけると嬉しいです。

https://youtrack.jetbrains.com/issue/CPP-30672/Support-specify-environment-variable-on-run-configurationyoutrack.jetbrains.com

参考

www.switch-science.com

mag.switch-science.com

www.jetbrains.com

platformio.org

#C100 ありがとうございました

コミックマーケット100、無事終わりました。 ご購入いただいた方々、また、スペースにお立ち寄りいただいた方々、ありがとうございました。

ありがたいことに新刊既刊とも完売してしまい*1、ご購入いただけなかった方々には申し訳ありませんでした。 既刊はともかく新刊は少し余裕を持ったつもりでしたが、すみません。

新刊『Unity Test Framework完全攻略ガイド 第2版』は、数日中にPDF版、またAmazonKindle版およびPOD版をリリース予定ですので、もうしばらくお待ち下さい。 下記記事でアナウンスしていきます。

www.nowsprinting.com

既刊『Unity Automated QA攻略ガイド』のPDF版はBOOTHおよび技術書典マーケットでご購入いただけます。 またこちらもAmazonKindle版およびPOD版のリリースを検討しています。

www.nowsprinting.com

所感

一般参加チケット販売数はC99の1.5倍、第7波&台風8号で回避された方もいるはずですが、もう少し多く感じました。西はVTuberとかコスプレROMもあったので回遊している人が多かったのかもしれないですが。 なにより、一般入場時間が3段階あったせいか、午後になってもそれほど閑散とすることなく人が流れていて、休憩(&買いもの)タイミングが難しい回でした。

スペースでは、知り合いの方もそうでない方も、Unityのテストに関して色々お話できたりして*2楽しく過ごせました。 ここ数年失われている勉強会の懇親会の雰囲気を思い出します。

最後になりましたが、この機会・この場を作ってくださった準備会の皆様、ありがとうございました。 そして遅刻してすみませんでした…

C101予定

準備期間が短いのが悩みどころですが、申し込む方向で検討中です。 引き続きUnityのテスト系で、統合テスト(integration testing)にフォーカスした本の構想があるので恐らくはそれで。

ぜひ、サークル「いか小屋」をお気に入り登録してお待ち下さい。

*1:既刊13:45ごろ、新刊14:25ごろ

*2:いただいたスペースがお誕生日席の外側で余裕あったためできたことです。また、距離を取ってお互いマスクをしていたとはいえ、感染症対策的には好ましくない行為ではありますが

Unity Test Framework完全攻略ガイド 第2版

Unityの標準テストフレームワークであるUnity Test Frameworkパッケージの解説本をコミックマーケット100の1日目(土曜日)西そ06b*1いか小屋」で頒布します。

昨年の技術書典11で頒布した第1版をベースに、テストダブルやテスト技法に関するセクションなどへの加筆修正のほか、プレビューリリースとされているUnity Test Frameworkバージョン2の変更点についても付録として収録しています。

想定読者

UnityでC#スクリプトを書けるレベルのソフトウェアエンジニア(プログラマー)を想定しています。 UnityやC#言語自体の解説はしていません。

ユニットテストに関しては入門レベルからカバーしています。

目次の紹介

各章の内容は次のとおりです。

第1章 テストとは何か

「テスト」と、ゲーム業界で使われる「デバッグ」という言葉の違いから、テストのターゲットに応じた考えかた、どのようなテストを自動化すべきか等。

第2章 Unity Test Frameworkの基本

Unity Test Frameworkパッケージを使ってユニットテストを書き、実行する手順を、順序立てて説明しています。 フォルダ(アセンブリ)構成や命名の指針も紹介しています。

第3章 Edit Modeテスト

主にロジックのテストに利用できるEdit Modeテストの特徴と、Edit Modeテスト固有の機能について。

第4章 Play Modeテスト

よりUnityプレイヤーに近い環境で動作させられるPlay Modeテストの特徴、Unityエディターおよびプレイヤーでの実行方法について。

第5章 非同期処理のテスト

コルーチンやUniTaskによるasyncメソッドのテストについて。

第2版では、Unity Test Framework v2.0でasync/awaitが使用できるようになることを踏まえ、表記の見直しを行ないました

v2.0.1(C101版)では、Unity Test Framework v1.3でasync/awaitのテストが前倒しサポートされたことを追記しました。 詳しくは次の記事を参照してください。[2023/1/1追記]

www.nowsprinting.com

第6章 アサーション

NUnit3の制約モデル(Assert.That)によるアサーション(テスト実行結果の検証)について、検証対象や目的に応じた最適な書きかたやTips、アンチパターンを紹介。

第7章 テストダブル

テスト対象が何らかのコンポーネントに依存しているとき(内部で生成した疑似乱数やサーバからのレスポンスに応じて結果が変わるなど)、その依存コンポーネントを偽物(スタブ、スパイ、モックなど)に置き換えてテストするテクニックの紹介。

第2版では、シーケンス図の見直し、サンプルコード掲載*2、NSubstituteについて詳細化を行ないました

第8章 パラメタライズドテスト

ひとつのメソッドを引数を様々に変えてテストしたいとき、NUnit3のTestCase属性・TestCaseSource属性・Values属性・ValueSource属性を使ってこれを実現する方法。

第9章 Unity Test Framework Tips

Unity Test Frameworkに備わる機能の紹介。 許容誤差付きのアサーション(EqualityComparer)、GCメモリアロケーション検知(AllocatingGCMemory)、ログ出力の検証(LogAssert)、テストの前処理・後処理(SetUp/TearDown属性, OneTimeSetUp/TearDown属性, PrebuildSetup/Cleanup属性, ICallbacks/IErrorCallbacksインタフェース)、アセットのロード、実行プラットフォーム制限(UnityPlatform属性)、カテゴライズ(Category属性)、タイムアウトの指定(Timeout属性)、MonoBehaviourのテストなど。

第2版では、UnitySetUp/TearDown属性のセクションを追加しました*3

第10章 Sceneを使用するテスト

Edit Modeテスト・Play Modeテスト(エディター実行)・Play Modeテスト(プレイヤー実行)それぞれで、Sceneファイルをロードしてテストに使用する方法。 "Scenes in Build"に含まれないSceneをロードする方法も紹介しています。

第11章 UPMパッケージのテスト

Unity Package manager(UPM)パッケージのテストを実装・実行する方法。

第12章 テストの実行方法

UnityエディターのTest Runnerウィンドウ、Jet Brains Rider、コマンドラインからのテスト実行方法・オプションの紹介。

第13章 コードカバレッジ

Unity Code Coverageパッケージを用いてコードカバレッジ(テストがプロダクトコードのどの部分をカバーしているかの指標)を採取する方法。 また、RiderのdotCoverプラグインの紹介。

v2.0.1(C101版)では、Code Coverageパッケージv1.2で変更されたコマンドライン引数に対応しています。詳しくは公式のUpgrade Guideを参照してください。[2023/1/1追記]

docs.unity3d.com

第14章 テスト実装のヒント

壊れやすい「実装のテスト」ではなく、変更に強い「仕様のテスト」を書くためのアプローチを紹介しています。 テストケースを導出するためのテスト技法(同値分割法・境界値分析・デシジョンテーブル・組み合わせテスト)、再現テスト、テスト駆動開発(TDD)。

第2版では、各テスト技法のモデル図、状態遷移テストおよび学習テストのセクションを追加しました

付録A JetBrains Rider Tips

IDEにRiderを使う場合の、コグニティブ複雑度の計測(CognitiveComplexityプラグイン)、テスト用Live Templateの紹介。

付録B OpenUPM-CLI

本書で紹介しているUPMパッケージをUnityプロジェクトにインポートするのに便利なopenupm-cliコマンドの紹介。

付録C Unity Test Framework v2.0

第2版で追加しました。現在プレビュー中であるv2.0の主な変更点をまとめています*4

変更点を特記していない章も、前提バージョンの変更や記述の見直しなどを実施しています。 ページ数では134から152と18ページ増ですが、原稿の行ベースで見ると約35%書き換わっています(git diff調べ)。

サンプルコード

サンプルコードは引き続き下記リポジトリを参照してください。

github.com

表紙について

C99の『Unity Automated QA攻略ガイド』に続き、りょふ彦(@ryofu1323)さんにファンタジーRPG風ユニティちゃんをお願いしました。

デフォルメも等身もかわいく描いていただけて、大変ありがたいです。

信販売・電子版について

[8/17] BOOTHにて電子版のアップデート配信を開始しました。 物理本は在庫なしです。ご希望の方は「入荷お知らせメールを受け取る」をクリックしてお待ち下さい。需要次第で増刷を検討します。 物理本の在庫復活しています[2023/1/1]

ikagoya.booth.pm

[8/22] 技術書典オンラインマーケットにて電子版のアップデート配信を開始しました。

techbookfest.org

なお、近日中にAmazonKindle版およびPOD版のリリースも予定しています。

その他

第1版の内容についてはこちらの記事を参照してください。

www.nowsprinting.com

既刊の『Unity Automated QA攻略ガイド』も若干数ですが持っていきます。 内容はこちらの記事を参照してください。

www.nowsprinting.com

[2023/5] 続編として『統合テスト編』を上梓しました。内容はこちらの記事を参照してください。

www.nowsprinting.com

*1:また日本Androidの会Unity部さんのお隣です! UNIBOOK14のついでにお立ち寄りください

*2:サンプルコードリポジトリには例がありましたが、今回一部変更した上で本文にも掲載しています

*3:なぜか漏れていました…

*4:元々の予定ではUnity 2022.2から正式採用のはずだったのですが、5月に延期がアナウンスされました

Unity x WebGLのテストTips

今週は、Unity 1週間ゲームジャム 第22回が開催されています。 今回のお題は「そろえる」とのことで、何かをそろえたら消えて得点が入るような振る舞いの自動テストを書きたくなる方もいるのではないでしょうか。

Unity 1週間ゲームジャムの投稿先であるフリーゲーム投稿サイトunityroomには、プラットフォームに「WebGL」を選択してビルド・アップロードを行ないます。 WebGLプラットフォームには、一部Unity Test Frameworkの制限があります。そのあたりを改めて検証してみました。

検証環境

  • Unity 2021.3.1f1
  • Unity Test Framework 1.1.31

Unity 2020以前の場合、本記事に記載していない問題を確認しています*1。 また、Unity 2021.3ではWebGLのビルドがかなり早くなっています*2。 どうしても古いバージョンを使わなければならない事情でもない限り、Unity 2021.3で開発することをおすすめします。

Edit ModeテストおよびIn Editor実行の制限

Edit Modeテストおよび、Play ModeテストをUnityエディター上で実行する限り、プラットフォームがWebGLであっても制限はありません。 そしてほとんどの場合、テストはUnityエディター上で動作させれば十分です。 つまり、

勝ったッ! 第3部完!

ということで、以下はおまけです。

プレイヤー実行の制限

本セクションの制限事項は、Unityエディター側の対応により Unity 2020.3.42f1, 2021.3.8f1, 2022.1.12f1, 2022.2.0b3, 2023.1.0a4 では解消しています。 詳しくは次の記事を参照してください [2023/1/6]

www.nowsprinting.com

Test Runnerウィンドウで「PlayMode」を選択しているとき、ウィンドウ右上にある「Run All Tests(WebGL*3)」をクリックすると下図のようにWebブラウザが開き、その中でWebGLアプリケーションとしてテストが実行されます。

以下、WebGLプレイヤー実行における問題点と制限事項を紹介します。

テスト実行結果がTest Runnerウィンドウに反映されない

他のプラットフォームではテスト実行結果はTest Runnerウィンドウに反映されますが、WebGLではされません。 "Receiving test data from player" と表示されるプログレスバーが止まったままとなり、キャンセルするしかありません。

どのテストが失敗したかを知るには、ブラウザ上のメッセージを読み取るか、WebブラウザJavaScriptコンソールでログを読むしかないようです。

docs.unity3d.com

UnityTest属性を使用できない

これはUnity Test Frameworkのドキュメントにも記載されている公の制限事項です。 UnityTest属性による非同期テストをWebGLプレイヤーで実行すると、 "Unhandled log message: '[Error] [xxx] The message header is corrupted and for security reasons connection will be terminated.'. Use UnityEngine.TestTools.LogAssert.Expect" というメッセージで失敗します。これはテスト実行中にLogErrorが吐かれたことに起因するメッセージであり、肝心のLogErrorで何が書かれているかは読み取れません。

この問題を回避するには、UnityTest属性で書かれたテストをWebGLプレイヤーで実行しないよう、次のようにUnityPlatform属性のexcludeパラメーターで除外指定します。

[UnityTest]
[UnityPlatform(exclude = new[] { RuntimePlatform.WebGLPlayer })]
public IEnumerator 非同期テストメソッド()
{
    _cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
    yield return null;

    Assert.That((bool)_cube, Is.True);
}

このテストはWebGLプレイヤーでは実行されなくなります。同等のテストをWebGLプレイヤーでも実行したいのであれば、なんとか同期テストとして書き直すしかありません。

UnitySetUp属性を使用できない

これも恐らくUnityTest属性と同じ理由で使用できないようです。 しかし、UnityTearDown属性は(Unity Test Framework v1時点では)WebGLでも動作します。ふしぎ。

こちらはSetUpメソッドのみ除外指定しても意味ないはずなので、UnityPlatform属性をテストクラスにつけることでクラスごと除外指定するとよいでしょう。

なお、UnityPlatform属性はメソッド、クラスのほか、アセンブリにも指定できます。

Unity Test Framework v2での制限

続いて、Unity Test Framework 2.0.1-pre.18を使っての検証結果です。プレリリース版ですので、今後緩和される可能性はあります。

非同期テストのサポートが最大の売りであるUnity Test Framework v2ですが、上記v1の制限事項は引き続き残り、追加で以下の制限があります。

非同期テストは使用できない

v2で追加された非同期テスト(async tests)ですが、これもWebGLでは使用できないようです。

UnityTearDown属性を使用できない

v1ではなぜか使用できたUnityTearDown属性は、v2では使用できなくなったようです。

まとめ

WebGLのテストは、Unityエディター上で実行する限り問題なし!

参考

Unity Test Frameworkについてはこちらの同人誌を参照してください。Unity Test Framework v2対応となる第2版に無償アップデートを予定していますので、買い控える必要はありません!

www.nowsprinting.com

Unity Test Framework v2についてはこちらを参照。

www.nowsprinting.com

*1:発生条件や修正されたバージョンの特定まで行なう時間がなく未調査です

*2:これも正確にどのバージョンからかは未調査です

*3:WebGL」部分は現在選択されているプラットフォーム名が入ります

diff-pdfでPDFファイルの視覚的差分を取るGitHub Action

diff-pdfは、オープンソースのPDFファイル比較ツールです。 これをGitHub Actionsワークフローから使用するためのDockerイメージ及びActionを作りましたので紹介します。

github.com

diff-pdfには、指定するオプションによって大きく2通りの用途があります。

  1. PDFを出力するシステムの検証のため、出力されたPDFが期待するものと等しいものかを検証する(ビジュアルリグレッションテスト、ゴールデンテスト)
  2. Re:VIEWMarpのようなテキストベースの原稿をPDFなどにレンダリングするツールを使用して執筆している場合に、テキストでなくレンダリング結果の差分*1を確認するためのPDFを生成する

以下、それぞれの用途に向けたワークフローの記述例を紹介します。

PDFが期待するものと等しいかを検証する

あらかじめ用意したexpected.pdfと、システムで出力したactual.pdfを次のように指定します。

- uses: nowsprinting/diff-pdf-action@v1
  with:
    file1: expected.pdf
    file2: actual.pdf

PDF同士が不一致のとき、このステップはエラーとなります。

差分PDFを生成する

例えばPull Request(以下PR)における差分を確認したいときは、PRのHEADコミットとBASEコミットそれぞれでレンダリングを実行し、得られたhead.pdfbase.pdfを次のように指定します。

- uses: nowsprinting/diff-pdf-action@v1
  with:
    file1: base.pdf
    file2: head.pdf
    options: --output-diff=diff.pdf
    suppress-diff-error: true

suppress-diff-error: trueと指定することにより、2つのPDFが不一致であってもこのステップはエラーになりません。 そして--output-diffオプションで指定したパスに、次のような赤青に色付けされた差分PDFファイルが出力されます。

なお、--skip-identicalオプションを付けると差分のないページを省略したPDFファイルが出力されて便利です。 また--mark-differencesを付けると上図のようにページ左に差分箇所を示す赤いマークが出力されます。

PRの差分PDFを出力するワークフロー全文

参考までに、PRにラベルdiff-pdfを付けたときのみ差分PDFファイルを出力するワークフローの例を貼っておきます。 このままコピペすれば動きます。

name: Diff pdf

on:
  pull_request:
    types: [ opened, synchronize, reopened, labeled ] # Note: labelを外した契機では動かない

env:
  REVIEW_CONFIG_FILE: 'config.yml'  # 代表的な設定ファイルを指定

jobs:
  diff-pdf:
    if: |
      contains(github.event.pull_request.labels.*.name, 'diff-pdf') ||
      ((github.event.action == 'labeled') && (github.event.label.name == 'diff-pdf'))
    runs-on: ubuntu-latest
    container:
      image: vvakame/review:5.3 # 原稿のRe:VIEWバージョンと一致するイメージを指定

    steps:
      - name: Checkout head
        uses: actions/checkout@v3
        with:
          fetch-depth: 100

      - name: Build head PDF
        run: |
          REVIEW_CONFIG_FILE=${{ env.REVIEW_CONFIG_FILE }} rake pdf
          mv ./*.pdf ../head.pdf
        working-directory: articles

      - name: Checkout base
        run: |
          git config --system --add safe.directory /__w/REPO_NAME/REPO_NAME # REPO_NAMEにはリポジトリ名を記述
          git checkout ${{ github.event.pull_request.base.sha }}

      - name: Build base PDF
        run: |
          REVIEW_CONFIG_FILE=${{ env.REVIEW_CONFIG_FILE }} rake pdf
          mv ./*.pdf ../base.pdf
        working-directory: articles

      - name: Diff PDFs
        uses: nowsprinting/diff-pdf-action@v1
        with:
          file1: base.pdf
          file2: head.pdf
          options: --verbose --skip-identical --mark-differences --output-diff=diff.pdf --dpi=100
          suppress-diff-error: true
        # Note: `--skip-identical`は差分が無い場合でも、空白1ページのpdfが出力される

      - uses: actions/upload-artifact@v3
        with:
          name: Output diff PDF
          path: diff.pdf

コンテナで使用する

diff-pdf-actionで使用しているDockerイメージはGitHub Container Registryに公開しています*2

コンテナは次のように使用できます。 diff-pdfを何度も実行するワークフローであれば、コンテナを使用するほうがオーバーヘッドが抑えられて高速になるはずです。

jobs:
  diff:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/nowsprinting/diff-pdf:latest

    steps:
      - uses: actions/checkout@v3
      - run: diff-pdf --verbose expected1.pdf actual1.pdf
      - run: diff-pdf --verbose expected2.pdf actual2.pdf
      - run: diff-pdf --verbose expected3.pdf actual3.pdf
      :

*1:PDF比較ツールには、テキストを比較するものが比較的多くありますが、diff-pdfは視覚的に(画像として)比較してくれるツールです

*2:Docker Hubには置いていないので注意