You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:31:02