0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

InputSystem を用いたテストをRunAllした際にテストが失敗する対策

Posted at

概要

TestRunner にて InputSystem を用いた入力処理のテストを InputTestFixture を継承したテストクラスを用いて行った際
個別実行したときには全てのテストが通過するにもかかわらず RunAll で実行すると最初のテスト以外すべて失敗するという現象にぶちあったのでメモ

構成

Unityバージョン : 6000.0.31.f1
TestFrameworkバージョン : 1.4.5
InputSystemバージョン : 1.11.2

現象の詳細

以下のコードのテスト、コード量が多くなるので二つのテストのみ

using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEditor.SceneManagement;
    
    
namespace Game.Player.Tests
{
    public class PlayerMoveTests : InputTestFixture
    {

        private Keyboard _keyboard;
        
        [SetUp]
        public override void Setup()
        {
            base.Setup();
            if (_keyboard == null)
            {
                Debug.Log("初期設定");
                _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
            }
        }


        [TearDown]
        public override void TearDown()
        {
            base.TearDown();
        }


        [Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")]
        [UnityTest]
        public IEnumerator Test_PlayerMove_ArrowKeys_Correct()
        {
            yield return LoadTestScene();
            yield return new WaitForSeconds(1f);

            var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];

            // 上下左右の入力通りに InputMove に値が入るか否か.
            Press(_keyboard.upArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.up);
            Release(_keyboard.upArrowKey);
            InputSystem.Update();

            Press(_keyboard.downArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.down);
            Release(_keyboard.downArrowKey);
            InputSystem.Update();
            
            Press(_keyboard.leftArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.left);
            Release(_keyboard.leftArrowKey);
            InputSystem.Update();

            Press(_keyboard.rightArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.right);
            Release(_keyboard.rightArrowKey);
            
            GameObject.DestroyImmediate(player.gameObject);
        }

        
        [Description("WASD キー入力で正常に InputMove に値が入るか.")]
        [UnityTest]
        public IEnumerator Test_PlayerMove_WASDKeys_Correct()
        {           
            yield return LoadTestScene();
            yield return new WaitForSeconds(1f);

            var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];

            // 上下左右の入力通りに InputMove に値が入るか否か.
            Press(_keyboard.wKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.up);
            Release(_keyboard.wKey);
            InputSystem.Update();

            Press(_keyboard.sKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.down);
            Release(_keyboard.sKey);
            InputSystem.Update();
            
            Press(_keyboard.aKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.left);
            Release(_keyboard.aKey);
            InputSystem.Update();

            Press(_keyboard.dKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.right);
            Release(_keyboard.dKey);
            
            GameObject.DestroyImmediate(player.gameObject);
        }
        
        // コード量が多くなるので省略.

        private AsyncOperation LoadTestScene()
        {
            return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single));
        }
    }
}

このコードでテストを RunAll すると以下の画像のように先頭のテストのみ成功し、後ろのテストは全て失敗する現象が発生した

image.png

エラーコード

ArgumentOutOfRangeException: Cannot be negative
Parameter name: value

似たような不具合が発生していたようだがこちらでは解決していなかった
https://discussions.unity.com/t/error-when-running-all-unit-tests/886736/7

ただ、この中でシーン上にある PlayerInput を含むオブジェクトを削除したらうまくいくとの書き込みがあり試しにシーン上にあるオブジェクトを TearDown 時に削除するようにしてみた

試行錯誤 ( テストは失敗する )

using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEditor.SceneManagement;


namespace Game.Player.Tests
{
    public class PlayerMoveTests : InputTestFixture
    {

        private Keyboard _keyboard;
        

        [SetUp]
        public override void Setup()
        {
            base.Setup();
            if (_keyboard == null)
            {
                _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
            }
        }


        [TearDown]
        public override void TearDown()
        {
            CleanUp();
            base.TearDown();
        }


        [Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")]
        [UnityTest]
        public IEnumerator Test_PlayerMove_ArrowKeys_Correct()
        {
            yield return LoadTestScene();
            yield return new WaitForSeconds(1f);

            var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];

            // 上下左右の入力通りに InputMove に値が入るか否か.
            Press(_keyboard.upArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.up);
            Release(_keyboard.upArrowKey);
            InputSystem.Update();

            Press(_keyboard.downArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.down);
            Release(_keyboard.downArrowKey);
            InputSystem.Update();
            
            Press(_keyboard.leftArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.left);
            Release(_keyboard.leftArrowKey);
            InputSystem.Update();

            Press(_keyboard.rightArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.right);
            Release(_keyboard.rightArrowKey);
        }

        
        [Description("WASD キー入力で正常に InputMove に値が入るか.")]
        [UnityTest]
        public IEnumerator Test_PlayerMove_WASDKeys_Correct()
        {           
            yield return LoadTestScene();
            yield return new WaitForSeconds(1f);

            var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];

            // 上下左右の入力通りに InputMove に値が入るか否か.
            Press(_keyboard.wKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.up);
            Release(_keyboard.wKey);
            InputSystem.Update();

            Press(_keyboard.sKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.down);
            Release(_keyboard.sKey);
            InputSystem.Update();
            
            Press(_keyboard.aKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.left);
            Release(_keyboard.aKey);
            InputSystem.Update();

            Press(_keyboard.dKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.right);
            Release(_keyboard.dKey);
        }
        

        // 省略.        

        private AsyncOperation LoadTestScene()
        {
            return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single));
        }

        
        // シーン上にあるオブジェクトを全て破棄する.
        private void CleanUp()
        {
            var activeScene = SceneManager.GetActiveScene();
            foreach (var go in activeScene.GetRootGameObjects())
            {
                GameObject.DestroyImmediate(go);
            }
        }
    }
}

このコードにしてもエラーが出てテストが失敗したのであきらめかけていたがエラーコードの内容が

ArgumentOutOfRangeException: Cannot be negative
Parameter name: value

から

System.ArgumentNullException : Value cannot be null.
Parameter name: source

に変わっていたので、シーン上にあるオブジェクトを削除したのが原因 -> _keyboard が null のままになってると推測を立てて
SetUp() 時に毎回 _keyboard に値を入れ直すようにしたところ RunAll でテストが全て通過するようになった

        [SetUp]
        public override void Setup()
        {
            base.Setup();
            //if (_keyboard == null)
            //{
                _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
            //}
        }


2回目以降は _keyboard == null で _keyboard 自体は null じゃなくて内部で利用している何かしらのインスタンスが null になってた?
ような挙動っぽい

解決したコード

using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEditor.SceneManagement;


namespace Game.Player.Tests
{
    public class PlayerMoveTests : InputTestFixture
    {

        private Keyboard _keyboard;
        
        
        [SetUp]
        public override void Setup()
        {
            base.Setup();
            _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
        }


        [TearDown]
        public override void TearDown()
        {
            // TearDown前にシーン上のオブジェクトを全て破棄.
            CleanUp();
            base.TearDown();
        }


        [Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")]
        [UnityTest]
        public IEnumerator Test_PlayerMove_ArrowKeys_Correct()
        {
            yield return LoadTestScene();
            yield return new WaitForSeconds(1f);

            var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];

            // 上下左右の入力通りに InputMove に値が入るか否か.
            Press(_keyboard.upArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.up);
            Release(_keyboard.upArrowKey);
            InputSystem.Update();

            Press(_keyboard.downArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.down);
            Release(_keyboard.downArrowKey);
            InputSystem.Update();
            
            Press(_keyboard.leftArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.left);
            Release(_keyboard.leftArrowKey);
            InputSystem.Update();

            Press(_keyboard.rightArrowKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.right);
            Release(_keyboard.rightArrowKey);
        }

        
        [Description("WASD キー入力で正常に InputMove に値が入るか.")]
        [UnityTest]
        public IEnumerator Test_PlayerMove_WASDKeys_Correct()
        {           
            yield return LoadTestScene();
            yield return new WaitForSeconds(1f);

            var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0];

            // 上下左右の入力通りに InputMove に値が入るか否か.
            Press(_keyboard.wKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.up);
            Release(_keyboard.wKey);
            InputSystem.Update();

            Press(_keyboard.sKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.down);
            Release(_keyboard.sKey);
            InputSystem.Update();
            
            Press(_keyboard.aKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.left);
            Release(_keyboard.aKey);
            InputSystem.Update();

            Press(_keyboard.dKey);
            InputSystem.Update();
            Assert.AreEqual(player.MoveInput, Vector2.right);
            Release(_keyboard.dKey);
        }
 

        private AsyncOperation LoadTestScene()
        {
            return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single));
        }


        private void CleanUp()
        {
            var activeScene = SceneManager.GetActiveScene();
            foreach (var go in activeScene.GetRootGameObjects())
            {
                GameObject.DestroyImmediate(go);
            }
        }
    }
}

テストが全て通過するようになった
image.png

今後のバージョンアップでシーン上のオブジェクトを全削除しなくてもよくなってほしい...

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?