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 in Unity でのテスト方 法 EditModeTest == Zenject Unit Test

    エディタ上で動くテスト PlayModeTest == Zenject Integration Test シー ン上で動作確認できるテスト
  2. Foo がIBar に依存してる処理( 通常) public class Foo { public IBar

    bar; public int CallBar() // このメソッドをテストしたい { return bar.Bar() * 2; } } public interface IBar { int Bar(); }
  3. 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 に流し込むモック用クラスを作る必要がある
  4. 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(); }
  5. [TestFixture] public class FooUnitTest : ZenjectUnitTestFixture { [Test] public void

    CallBarTest() { var mock = new Mock<IBar>(); mock.Setup(b => b.Bar()).Returns(5); Container.BindInstance(mock.Object); Container.Bind<Foo>().FromNew().AsSingle(); var foo = Container.Resolve<Foo>(); Assert.AreEqual(10, foo.CallBar()); } } Container 経由でインスタンスを生成できる Container のモック機能が使える
  6. Mock<T> 便利 Moq ライブラリが提供する機能 https://github.com/moq モックを簡単に流し込めるのでテストが楽 OptionalExtras に入ってる // 何も返さなくていいとき

    Container.Bind<IBar>().FromMock() // 明示的にメソッドの返り値をモックしたい時 var mock = new Mock<IBar>(); mock.Setup(b => b.Bar()).Returns(5); Container.BindInstance(mock.Object);
  7. MonoBehavior が必要な処理 public class SpaceShip : MonoBehaviour { public Vector3

    Velocity { get; set; } public void Update() { transform.position += Velocity * Time.deltaTime; } } Velocity が設定されたら動くかテストしたい
  8. public class SpaceShipPlaymodeTest { [UnityTest] public IEnumerator SpaceShipPlaymodeTestWithEnumeratorPasses var spaceShip

    = new GameObject("ship") .AddComponent<SpaceShip>(); spaceShip.Velocity = Vector3.up; Assert.AreEqual(spaceShip.transform.position, Vector3.ze yield return new WaitForSeconds(1F); Assert.Greater(spaceShip.transform.position.y, 0F); } }
  9. MonoBehavior が必要な処理(Zenject) public class SpaceShip : MonoBehaviour { [Inject] public

    Vector3 Velocity { get; private set; } public void Update() { transform.position += Velocity * Time.deltaTime; } }
  10. Zenject in PlayMode public class SpaceShipTests : ZenjectIntegrationTestFixture { [UnityTest]

    public IEnumerator TestVelocity() { PreInstall(); Container.Bind<SpaceShip>() .FromNewComponentOnNewGameObject() .AsSingle().WithArguments(Vector3.up); PostInstall(); var spaceShip = Container.Resolve<SpaceShip>(); Assert.AreEqual(spaceShip.transform.position, Vector3.ze yield return null; Assert.Greater(spaceShip.transform.position.y, 0F); } }
  11. ZenjectIntegrationTestFixture がDI するためにいい感 じのメソッドを持っている PreInstall テスト用の準備をしてくれる Validation とかContext の生成とか PostIntall

    バインドしたオブジェクトを注入する Run all in player には対応しておらず実機では走 らないので注意
  12. 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<FooWindow>( } }