Event simulation for unit tests

For automated unit testing of custom UI elements it would be helpful to simulate certain actions a user would take - one being the click/tap on a certain UI element. How would one do this exactly?

I have found this documentation which unfortunately did not help me very much. The example for a KeyDown event simply reports 'EventType' does not contain a definition for 'KeyDownEvent' in Unity 2021.2.19f1.

But even if this was working - how would synthesizing a click event look like?

Hello Seyfe,

You are right the sample has not the right even type. it should be ‘EventType.KeyDown’.

In your case to simulate a click event you could use :

public static EventBase MakeMouseEvent(EventType type, Vector2 position, MouseButton button = MouseButton.LeftMouse, EventModifiers modifiers = EventModifiers.None, int clickCount = 1)
{
    var evt = new Event() { type = type, mousePosition = position, button = (int)button, modifiers = modifiers, clickCount = clickCount};
    if (type ==
        EventType.MouseUp)
        return PointerUpEvent.GetPooled(evt);
    else if (type ==
             EventType.MouseDown)
        return PointerDownEvent.GetPooled(evt);
    return null;
}

// in you code on the VisualElement : uielement:
using (EventBase mouseDownEvent = MakeMouseEvent(EventType.MouseDown, uielement.layout.center))
    uielement.SendEvent(mouseDownEvent);
using (EventBase mouseUpEvent = MakeMouseEvent(EventType.MouseUp, uielement.layout.center)) 
    uielement.SendEvent(mouseUpEvent);

If it work in your case you might also create an helper fonction in your code that handle Down/Up to reduce the amount of code in your test.

Let me know if you need more details.

Have a nive day

3 Likes

Thank you for the very fast response, @benoitm-unity ! It worked indeed as expected. For others if they run into problems: I had pretty much the same, but I had

evt.target = uielement;

set, because, idk, it seemed plausible to me. With this property set it didn’t work though.

Thanks for the return.

You have right you can fix it with that line or by changing “uielement” in “uielement.SendEvent(mouseDownEvent and mouseUpEvent);” by the root element of the windows/panel like “myroot.SendEvent…”"

Lets me know if it fully fit your needs

1 Like

Thanks! With the above approach it currently works well enough for the unit tests!

Maybe to add to this, @benoitm-unity :

What if I want to simulate a mouse click at the position of an element to test that it actually receives the click, i.e., is not covered by another element or something else?

Hi there, other Benoit here with some more answers.

setting evt.target = uielement is not something you should do in general, as it forces the event to go directly to that element regardless of its position. Also it’s not super reliable, each event type has a potentially different policy on how to calculate their target and how to deal with a pre-set target like that. Some events will just ignore their pre-set target and recalculate their own target, and some other events will use it even if it doesn’t really make sense (pointer events on an element that has pickingMode = Ignore, for example).

Second point. The API for sending an event is someElement.SendEvent(someEvent), but that does not set the target of someEvent to someElement. In fact, the only thing it does is it sends the event to the element’s panel, where it will search for a proper target and propagate though the hierarchy accordingly. It could end up targetting some other element and completely ignore someElement.

@seyfe if you want to test that your element actually receives the click, just don’t set evt.target (or set it to null, which it is by default) but do set the position where the click is happening. Then you can send the event using panel.visualTree.SendEvent(evt) if you know what panel you want to send it to, just so you’re clear you’re not sending it to the element itself but really you’re sending it to the entire panel. What you’re trying to do is pretty much the default behavior for all events, i.e. not setting the target will make it choose its own target thus testing if the element is covered by another element etc.

Hi other Benoit! Thanks for clarifying things. Also, I would support your fight for Markdown usage in the forums :wink:

One thing to add to the answer of @benoitm-unity : As far as I understand, if you were to write a click method on arbitrary objects, you might want to use uielement.worldBound.center instead of uielement.layout.center to get the coordinates of the click position right.

1 Like

Sorry to bump this thread.

I’m trying to simulate ClickEvent on certain mouse position using the above approach. With MouseUpEvent/MouseDownEvent everything works as described, but with ClickEvent it always triggering on the element under current mouse position.

Is this behaviour expected, or am I doing something wrong?

var root = uiDocument.rootVisualElement;

var evt = new Event()
{
    type = EventType.MouseUp,
    mousePosition = new Vector2(-100, -100),
    button = 0,
    pointerType = UnityEngine.PointerType.Mouse,
};

using (EventBase clickEvent = ClickEvent.GetPooled(evt))
{
    root.panel.visualTree.SendEvent(clickEvent);
}