Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Zenjectとテスト

いも
March 20, 2018

 Zenjectとテスト

Gotanda.unity #5で発表するやつです

いも

March 20, 2018
Tweet

More Decks by いも

Other Decks in Programming

Transcript

  1. Zenject
    でのテスト
    Gotanda.unity #5 2018/03/20

    View Slide

  2. 自己紹介
    いも(@adarapata)
    ミクシィ XFRAG
    ゲー
    ムプログラマ
    入社三日目
    最近GodotEngine
    が楽しい

    View Slide

  3. 今日の内容
    Unity
    標準のテストの書き方と、Zenject
    を使って
    いる場合のテストの書き方の違いを知る。
    ※ Unity
    内でのテストコー
    ドが必要かどうかについて
    は今回は話しません。

    View Slide

  4. Zenject is
    https://github.com/modesttree/Zenject
    Unity
    で使えるDI
    フレー
    ムワー

    依存関係をすっきりさせてくれる
    かせさんの記事がわかりやすい
    http://yutakaseda3216.hatenablog.com/entry/201
    7/04/17/124612

    View Slide

  5. Unity
    のTest
    書いてますか?

    View Slide

  6. Unity
    でのテスト方法
    EditModeTest
    エディタ上で動くテスト
    PlayModeTest
    シー
    ン上で動作確認できるテスト

    View Slide

  7. Zenject in Unity
    でのテスト方

    EditModeTest == Zenject Unit Test
    エディタ上で動くテスト
    PlayModeTest == Zenject Integration Test
    シー
    ン上で動作確認できるテスト

    View Slide

  8. Foo
    がIBar
    に依存してる処理(
    通常)
    public class Foo {
    public IBar bar;
    public int CallBar() //
    このメソッドをテストしたい
    {
    return bar.Bar() * 2;
    }
    }
    public interface IBar
    {
    int Bar();
    }

    View Slide

  9. EditModeTest
    public class FooEditModeTest {
    class MockBar : IBar
    {
    public int Bar() { return 5; }
    }
    [Test]
    public void CallBarTest() {
    Foo foo = new Foo { bar = new MockBar() };
    Assert.AreEqual(10, foo.CallBar());
    }
    }
    Foo
    に流し込むモック用クラスを作る必要がある

    View Slide

  10. Foo
    がIBar
    に依存してる処理
    (Zenject)
    public class Foo {
    [Inject]
    public IBar bar { get; private set; }
    public int CallBar() //
    このメソッドをテストしたい
    {
    return bar.Bar() * 2;
    }
    }
    public interface IBar
    {
    int Bar();
    }

    View Slide

  11. [TestFixture]
    public class FooUnitTest : ZenjectUnitTestFixture
    {
    [Test]
    public void CallBarTest()
    {
    var mock = new Mock();
    mock.Setup(b => b.Bar()).Returns(5);
    Container.BindInstance(mock.Object);
    Container.Bind().FromNew().AsSingle();
    var foo = Container.Resolve();
    Assert.AreEqual(10, foo.CallBar());
    }
    }
    Container
    経由でインスタンスを生成できる
    Container
    のモック機能が使える

    View Slide

  12. Mock
    便利
    Moq
    ライブラリが提供する機能
    https://github.com/moq
    モックを簡単に流し込めるのでテストが楽
    OptionalExtras
    に入ってる
    //
    何も返さなくていいとき
    Container.Bind().FromMock()
    //
    明示的にメソッドの返り値をモックしたい時
    var mock = new Mock();
    mock.Setup(b => b.Bar()).Returns(5);
    Container.BindInstance(mock.Object);

    View Slide

  13. MonoBehavior
    が必要な処理
    public class SpaceShip : MonoBehaviour
    {
    public Vector3 Velocity
    {
    get; set;
    }
    public void Update()
    {
    transform.position += Velocity * Time.deltaTime;
    }
    }
    Velocity
    が設定されたら動くかテストしたい

    View Slide

  14. public class SpaceShipPlaymodeTest {
    [UnityTest]
    public IEnumerator SpaceShipPlaymodeTestWithEnumeratorPasses
    var spaceShip = new GameObject("ship")
    .AddComponent();
    spaceShip.Velocity = Vector3.up;
    Assert.AreEqual(spaceShip.transform.position, Vector3.ze
    yield return new WaitForSeconds(1F);
    Assert.Greater(spaceShip.transform.position.y, 0F);
    }
    }

    View Slide

  15. MonoBehavior
    が必要な処理(Zenject)
    public class SpaceShip : MonoBehaviour
    {
    [Inject]
    public Vector3 Velocity
    {
    get; private set;
    }
    public void Update()
    {
    transform.position += Velocity * Time.deltaTime;
    }
    }

    View Slide

  16. Zenject in PlayMode
    public class SpaceShipTests : ZenjectIntegrationTestFixture
    {
    [UnityTest]
    public IEnumerator TestVelocity()
    {
    PreInstall();
    Container.Bind()
    .FromNewComponentOnNewGameObject()
    .AsSingle().WithArguments(Vector3.up);
    PostInstall();
    var spaceShip = Container.Resolve();
    Assert.AreEqual(spaceShip.transform.position, Vector3.ze
    yield return null;
    Assert.Greater(spaceShip.transform.position.y, 0F);
    }
    }

    View Slide

  17. ZenjectIntegrationTestFixture
    がDI
    するためにいい感
    じのメソッドを持っている
    PreInstall
    テスト用の準備をしてくれる
    Validation
    とかContext
    の生成とか
    PostIntall
    バインドしたオブジェクトを注入する
    Run all in player
    には対応しておらず実機では走
    らないので注意

    View Slide

  18. 所感
    普通のテストとそんなにやり方は変わらない
    Zenject
    で書くべき!
    というかはInject
    してたら必
    然的にテストもContainer
    使わざるを得なくなるよ
    ねというお気持ち
    でもMoq
    をサクッと使えるのは便利かも

    View Slide

  19. 余談1
    unity-uitest
    https://github.com/taphos/unity-uitest
    uGUI
    周りのテストを書きやすくしてくれるライブ
    ラリ
    クリックイベント辺りをやりやすくしてくれる
    UITest
    を継承する必要があるので
    ZenjectIntegrationTest
    だと使いづらい

    View Slide

  20. public class TestExample : UITest
    {
    [UnityTest]
    public IEnumerator Example()
    {
    //
    シー
    ン読み込み
    yield return LoadScene("TestableScene");
    // Button
    オブジェクトを押したことにする
    yield return Press("Button");
    // Foo/Text
    のラベルの文字を確認する
    yield return AssertLabel("Foo/Text", "Pressed!");
    yield return Press("Close");
    // FooWindow
    コンポー
    ネントがついたオブジェクトが非アクティブになる
    yield return WaitFor(new ObjectDisappeared(
    }
    }

    View Slide

  21. 余談2
    RuntimeUnitTestToolkit
    https://github.com/neuecc/RuntimeUnitTestToolkit
    @neuecc
    さんの作ったテストライブラリ
    普通のシー
    ン上でテストを走らせてくれる
    アサー
    ションを再実装してシー
    ンで動くようにし
    ている
    シー
    ン上での動作なので実機での確認も可能

    View Slide

  22. View Slide