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

Unity UIToolkit运行模式单元测试中按钮点击事件未触发

Unity UIToolkit按钮单元测试点击事件不触发问题

问题描述

我用Unity的UIToolkit制作了一个简单菜单系统,核心是点击后切换场景的开始按钮。该按钮在测试场景中功能正常,但编写对应的运行模式单元测试时,测试无法检测到按钮的点击事件。

初始测试用例代码

[UnityTest]
public IEnumerator TestButtonClick()
{
    bool clicked = false;
    startButton.clicked += () =>
    {
        clicked = true;
        Debug.Log("Button clicked!"); // 用于检查点击事件是否触发的日志
    };


    startButton.SetEnabled(true);
    startButton.style.display = DisplayStyle.Flex;

    var pickingMode = startButton.pickingMode;
    Debug.Log($"Button picking mode: {pickingMode}");


    var displayStyle = startButton.resolvedStyle.display;
    Debug.Log($"Button display style: {displayStyle}");


    yield return null;

    var root = startButton.hierarchy.parent;
    if (root != null)
    {
        Debug.Log($"Button's parent is: {root}");
    }
    else
    {
        Debug.LogError("The button has no parent in the hierarchy.");
    }

    Debug.Log($"Button is interactable: {startButton.enabledSelf}");

    // 模拟PointerDownEvent
    using (var pointerDownEvent = new PointerDownEvent())
    {
        pointerDownEvent.target = startButton;  // 确保事件目标是正确的按钮
        startButton.SendEvent(pointerDownEvent);

        // 日志检查事件是否被接收
        Debug.Log($"PointerDownEvent target: {pointerDownEvent.target}");

        Assert.IsFalse(pointerDownEvent.isPropagationStopped, "PointerDownEvent传播意外停止。");
    }
    yield return new WaitForSeconds(0.1f); // 按下和抬起事件之间的短延迟

    // 模拟PointerUpEvent
    using (var pointerUpEvent = new PointerUpEvent())
    {
        pointerUpEvent.target = startButton;  // 确保事件目标是正确的按钮
        startButton.SendEvent(pointerUpEvent);

        // 日志检查事件是否被接收
        Debug.Log($"PointerUpEvent target: {pointerUpEvent.target}");

        Assert.IsFalse(pointerUpEvent.isPropagationStopped, "PointerUpEvent传播意外停止。");
    }



    yield return null;
    // 断言按钮点击事件已触发
    if (!clicked)
    {
        Debug.LogError("按钮点击事件未触发。");
    }

    // 断言按钮点击事件已触发
    Assert.IsTrue(clicked, "按钮点击事件未触发。");
}

测试日志输出

TestButtonClick (0.201s)
---
Unhandled log message: '[Error] 按钮点击事件未触发。'. Use UnityEngine.TestTools.LogAssert.Expect
---
UI_tests/<TestButtonClick>d__6:MoveNext () (at Assets/Tests/UI_tests.cs:121)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)


---
Button picking mode: Position
Button display style: Flex
Button's parent is: VisualElement Buttons (x:0.00, y:0.00, width:NaN, height:NaN) world rect: (x:0.00, y:0.00, width:NaN, height:NaN)
Button is interactable: True
PointerDownEvent target: Button Play (x:0.00, y:0.00, width:NaN, height:NaN) world rect: (x:0.00, y:0.00, width:NaN, height:NaN)
PointerUpEvent target: Button Play (x:0.00, y:0.00, width:NaN, height:NaN) world rect: (x:0.00, y:0.00, width:NaN, height:NaN)
按钮点击事件未触发。

菜单UXML代码

<engine:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:engine="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
<Style src="project://database/Assets/UI/MenuStyles.uss?fileID=7433441132597879392&amp;guid=446f70e76c5306241815d1b7f67264e1&amp;type=3#MenuStyles" />
<engine:VisualElement name="LeftSection" style="flex-grow: 1; background-color: rgb(0, 0, 0); width: 45%; height: 100%; margin-left: 50px;">
    <engine:VisualElement name="Buttons" style="flex-grow: 1; justify-content: center; padding-left: 20%; padding-right: 20%; padding-top: 20%; padding-bottom: 20%;">
        <engine:Button text="Play" name="Play" class="menu-button" />
        <engine:Button text="Settings" name="Settings" class="menu-button" />
        <engine:Button text="Rotate" name="Rotate" class="menu-button" />
        <engine:Button text="Exit" name="Exit" class="menu-button" />
    </engine:VisualElement>
</engine:VisualElement>
</engine:UXML>

后续测试对比

通过代码直接创建UIDocument和按钮时,测试能正常检测到点击事件;但加载自定义Menu.UXML的VisualTree时,点击事件无法触发。以下是两个对比测试用例:

正常通过的测试(代码创建UI)

[UnityTest]
public IEnumerator TestClick2()
{
    bool wasClicked = false;
    Button btn = new Button { name = "foobar" };
    btn.RegisterCallback<ClickEvent>(_ => wasClicked = true);
    GameObject hostGameObject = new GameObject();
    UIDocument uiDoc = hostGameObject.AddComponent<UIDocument>();
    uiDoc.panelSettings = ScriptableObject.CreateInstance<PanelSettings>();
    uiDoc.rootVisualElement.Add(btn);
    yield return null;

    using (ClickEvent clkEvt = ClickEvent.GetPooled())
    {
        clkEvt.target = btn;
        btn.SendEvent(clkEvt);
    }

    Assert.IsTrue(wasClicked);
}

失败的测试(加载UXML)

[UnityTest]
public IEnumerator TestClick3()
{
    var uiDocument = new GameObject().AddComponent<UIDocument>();
    // 加载指定的UXML文件
    var visualTree = Resources.Load<VisualTreeAsset>("Menu");
    if (visualTree != null)
    {
        uiDocument.visualTreeAsset = visualTree;
    }
    if (visualTree == null)
    {
        Debug.LogError("Visual Tree为空,无法从Resources加载");
    }

    yield return null;

    root = uiDocument.rootVisualElement;

    yield return null;

    startButton = root.Q<Button>("Play");
    if (startButton == null)
    {
        Debug.LogError("未找到Play按钮。");
    }
    Debug.Log("startButton是: " + startButton);
    Assert.NotNull(startButton, "start button为空");

    bool wasClicked = false;

    startButton.SetEnabled(true);
    startButton.style.display = DisplayStyle.Flex;
    
    startButton.RegisterCallback<ClickEvent>(_ => wasClicked = true);
    yield return null;

    using (ClickEvent clkEvt = ClickEvent.GetPooled())
    {
        clkEvt.target = startButton;
        startButton.SendEvent(clkEvt);
    }

    Assert.IsTrue(wasClicked);
}

内容的提问来源于stack exchange,提问作者Markus T.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 05:14:53