Hi. I’m trying to expand and improve my current HealthSystem I got in place, I want to test if UnitEvents are being called but I can’t find any information on how to unit test UnitEvents.
Here’s my code so far that fails, expected result should be true but actual are false.
I’m using NSubstitute.
// The test
[Test]
public void Checks_OnValueChanged_Event_Was_Called () {
INewDamageable<int> _damageable = Substitute.For<INewDamageable<int>>();
bool _wasCalled = false;
_damageable.OnValueChanged?.AddListener(() => _wasCalled = true);
_damageable.TakeDamage(1, null);
Assert.IsTrue(_wasCalled);
}
// Interface
public interface INewDamageable<T> {
void TakeDamage (T _amount, DataDamageType _damageType);
UnityEvent OnValueChanged { get; }
}
// Implementation
public class NewDamageable : MonoBehaviour, INewDamageable<int> {
public void TakeDamage (int _amount, DataDamageType _damageType) {
healthSystem.Damage(_amount);
OnValueChanged?.Invoke();
}
public UnityEvent OnValueChanged { get; } = new UnityEvent();
}
1 Like
I’m also interested in this issue. Does anyone have a solution for this?
Hi,
I came here with the same question, for me the solution below is working (TestRunner in EditMode) and that looks very much like your setup, but I’m not using Substitute, so perhaps the issue could be with that?
TestHealth.cs
public class TestHealth
{
[Test]
public void OnChangeHealth()
{
// Arrange
Health health = (new GameObject()).AddComponent<Health>();
health.SetMaxHealth(100);
health.SetCurrentHealth(80);
List<IntChange> invokes = new List<IntChange>();
health.OnHealthChanged.AddListener((IntChange change) =>
{
invokes.Add(change);
});
// Test
health.SetCurrentHealth(50);
health.SetCurrentHealth(99);
health.SetCurrentHealth(0);
// Assert
Assert.AreEqual(3, invokes.Count); // Event should have been called 3 times
Assert.AreEqual(80, invokes[0].OldValue);
Assert.AreEqual(50, invokes[0].NewValue);
Assert.AreEqual(50, invokes[1].OldValue);
Assert.AreEqual(99, invokes[1].NewValue);
Assert.AreEqual(99, invokes[2].OldValue);
Assert.AreEqual(0, invokes[2].NewValue);
}
[Test]
public void OnDie()
{
Health health = (new GameObject()).AddComponent<Health>();
health.SetMaxHealth(100);
health.SetCurrentHealth(80);
int invokes = 0;
health.OnDie.AddListener(() =>
{
invokes++;
});
health.SetCurrentHealth(0);
Assert.AreEqual(1, invokes); // Event should have been called 1 time
}
}
Health.cs
using UnityEngine;
using UnityEngine.Events;
public class Health : MonoBehaviour
{
private int _maxHealth = 1;
private int _currentHealth = 1;
public UnityEventIntChange OnHealthChanged { get; } = new UnityEventIntChange();
public UnityEvent OnDie { get; } = new UnityEvent();
// also works without { get; }, the events will then be visible in the Inspector
// public UnityEventIntChange OnHealthChanged = new UnityEventIntChange();
// public UnityEvent OnDie = new UnityEvent();
/* ... */
public void SetCurrentHealth(int amount)
{
IntChange change = new IntChange() { OldValue = _currentHealth };
_currentHealth = Mathf.Clamp(amount, 0, _maxHealth);
change.NewValue = _currentHealth;
OnHealthChanged?.Invoke(change);
if(_currentHealth == 0)
{
OnDie?.Invoke();
}
}
public void IncreaseHealth(int amount)
{
SetCurrentHealth(_currentHealth + amount);
}
public void DecreaseHealth(int amount)
{
SetCurrentHealth(_currentHealth - amount);
}
}
IntChange.cs
public class IntChange
{
public int NewValue;
public int OldValue;
public int Difference
{
get => NewValue - OldValue;
}
}
UnityEventIntChange.cs
using System;
using UnityEngine.Events;
[Serializable]
public class UnityEventIntChange : UnityEvent<IntChange> { }