Unity中如何让[SerializeField]在Play Mode Tests中正常工作?
Unity Play Mode测试中使用场景对象或正确加载对象的解决方案
环境
Unity版本:2020.3.30f1
问题概述
需要测试CreateGameControllerScript的功能,该脚本通过协程触发动画,依赖两个SerializeField引用(ToggleGroup和警告UI)。场景中已为这些引用赋值,但当前Play Mode Test直接新建GameObject并添加组件,导致引用为空,断言失败。旧版RunEditorUtilityInTheBackground()方法已失效,需找到正确的测试方式。
目标脚本核心代码
public class CreateGameControllerScript : MonoBehaviour { [SerializeField] public ToggleGroup select_Worlds; [SerializeField] public GameObject Warning_Not_AllOptions_Selected; private void Awake() { Assert.IsNotNull(select_Worlds); Assert.IsNotNull(Warning_Not_AllOptions_Selected); } public void StartGenerationOfInfiniteRunnerGame() { if (selectedWorldType != WorldType.None) { // 业务逻辑 } else { if (!select_Worlds.AnyTogglesOn()) { List<Toggle> myToggles = select_Worlds.GetComponentsInChildren<Toggle>().ToList(); myToggles.ForEach(tog => StartCoroutine(SmallWarningAnimation(tog))); } Warning_Not_AllOptions_Selected.SetActive(true); } } }
当前测试代码(存在引用为空问题)
[UnityTest] public IEnumerator CreateGameMainUITestsWithEnumeratorPasses() { // 旧方法已失效:var utility = RunEditorUtilityInTheBackground(); var gameObject = new GameObject(); var panelLogic = gameObject.AddComponent<CreateGameControllerScript>(); panelLogic.StartGenerationOfInfiniteRunnerGame(); yield return new WaitForSeconds(5); Assert.IsTrue(panelLogic.Warning_Not_AllOptions_Selected.activeSelf); }
解决方案
1. 直接加载包含目标对象的测试场景
利用Unity Test Framework的场景加载特性,直接加载已配置好引用的场景,可直接获取场景中已赋值的组件实例:
- 先将测试场景添加到Build Settings的
Scenes In Build列表中 - 在测试类上添加
[TestScene]属性指定场景名称,或在测试方法中手动加载场景
示例代码(使用[TestScene]属性)
[TestFixture] [TestScene("YourTestSceneName")] // 替换为你的测试场景名称 public class CreateGameControllerTests { [UnityTest] public IEnumerator TestWarningUIActivation() { // 等待场景加载完成 yield return null; // 获取场景中已配置好的组件实例 var panelLogic = Object.FindObjectOfType<CreateGameControllerScript>(); // 执行测试逻辑 panelLogic.StartGenerationOfInfiniteRunnerGame(); yield return new WaitForSeconds(5); Assert.IsTrue(panelLogic.Warning_Not_AllOptions_Selected.activeSelf); } }
示例代码(手动加载场景)
[UnityTest] public IEnumerator TestWarningUIActivation() { // 异步加载场景 yield return SceneManager.LoadSceneAsync("YourTestSceneName", LoadSceneMode.Additive); var panelLogic = Object.FindObjectOfType<CreateGameControllerScript>(); panelLogic.StartGenerationOfInfiniteRunnerGame(); yield return new WaitForSeconds(5); Assert.IsTrue(panelLogic.Warning_Not_AllOptions_Selected.activeSelf); // 测试完成后卸载场景 yield return SceneManager.UnloadSceneAsync("YourTestSceneName"); }
2. 实例化带有完整引用的Prefab
将包含CreateGameControllerScript及其依赖(ToggleGroup、警告UI)的对象制作成Prefab,并确保Prefab内的SerializeField已正确赋值。测试时直接实例化该Prefab即可获得完整引用:
示例代码
[UnityTest] public IEnumerator TestWarningUIActivation() { // 加载Prefab(需放在Resources文件夹下,或使用Addressables) var controllerPrefab = Resources.Load<GameObject>("Prefabs/CreateGameController"); var controllerInstance = Instantiate(controllerPrefab); var panelLogic = controllerInstance.GetComponent<CreateGameControllerScript>(); panelLogic.StartGenerationOfInfiniteRunnerGame(); yield return new WaitForSeconds(5); Assert.IsTrue(panelLogic.Warning_Not_AllOptions_Selected.activeSelf); // 清理实例 Destroy(controllerInstance); }
3. 手动构建测试依赖(独立测试)
如果需要测试逻辑的独立性,不依赖场景或Prefab,可以在测试方法中手动创建所有依赖对象并赋值:
示例代码
[UnityTest] public IEnumerator TestWarningUIActivation() { var gameObject = new GameObject(); var panelLogic = gameObject.AddComponent<CreateGameControllerScript>(); // 创建并配置ToggleGroup var toggleGroupObj = new GameObject("TestToggleGroup"); var toggleGroup = toggleGroupObj.AddComponent<ToggleGroup>(); // 添加一个未选中的Toggle var toggleObj = new GameObject("TestToggle", typeof(Toggle)); toggleObj.transform.SetParent(toggleGroupObj.transform); var toggle = toggleObj.GetComponent<Toggle>(); toggle.group = toggleGroup; toggle.isOn = false; // 创建警告UI对象 var warningObj = new GameObject("TestWarning"); warningObj.SetActive(false); // 赋值给组件的引用 panelLogic.select_Worlds = toggleGroup; panelLogic.Warning_Not_AllOptions_Selected = warningObj; // 执行测试逻辑 panelLogic.StartGenerationOfInfiniteRunnerGame(); yield return new WaitForSeconds(5); Assert.IsTrue(panelLogic.Warning_Not_AllOptions_Selected.activeSelf); // 清理测试对象 Destroy(gameObject); Destroy(toggleGroupObj); Destroy(warningObj); }
内容的提问来源于stack exchange,提问作者StarRuby
相关产品推荐
相关产品推荐

