Seit meinem letzten post vor 2h hab ich gefrühstückt und mir mal angeschaut, wie ich meine Actions testen kann. Was zuerst ausgesehen hat, als ob es total einfach wäre - stellte sich als komplexe Aufgabe heraus.
Denn die „netten“ Leute von PlayMaker haben wohl so überhaupt keinen Bock darauf, dass man deren Zeug weg mocken könnte und haben alles dicht gemacht. Also hab ich einen weiteren Layer einbauen müssen, welche meine Listener-actions nun nutzen. Bevor jetzt ein Event-Call an PlayMaker geht, geht dieser call erstmal durch meinen neuen Layer - welchen ich mocken kann. Denn mir war es einfach nicht möglich im Test abzufangen, ob meine Action jetzt einen neuen State triggern wird oder nicht -.-.
Als nächstes durfte ich mit dem Feld rumkämpfen, welches den Layer hält. Ist es public, dann wird es im Editor angezeigt - aber der Editor hat keinen support für den Typ also fliegt es mir um die Ohren. Wer das jetzt nicht nachvollziehen kann - nicht wichtig. Es war einfach nur nervig wie Sau, dass ich per Try-Error herausfinden musste, wie ich jetzt meinen Layer halten kann.
Nachdem ich das hatte, konnte ich ENDLICH einen Test grün bekommen.
Zufrieden damit war ich aber noch immer nicht. Denn ich wollte nicht für jeden Signal-Listener einen eigenen Test aufsetzen. Da sich diese kaum unterscheiden würden hab ich mich für eine parameterisierte Variante entschieden.
Mein Test schaut dann so aus:
using FakeItEasy;
using Game.DI;
using Game.FSM;
using Game.FSM.SignalListener;
using HutongGames.PlayMaker;
using NUnit.Framework;
using Zenject;
namespace Tests.UnitTests.Game.FSM.SignalListener {
public class SignalListenerTest : ZenjectUnitTestFixture {
private IFsmUtil util;
private FsmEvent fsmEvent;
public override void Setup() {
base.Setup();
SignalInstaller.Install(Container);
util = A.Fake<IFsmUtil>();
fsmEvent = new FsmEvent("Generic FSM Event");
}
[TestCaseSource(typeof(SignalListenerTestCases), nameof(SignalListenerTestCases.Signals))]
public void CheckSignals(SignalAction action, object signal) {
SetupAction(action);
action.OnEnter();
FireSignal(signal);
A.CallTo(
() => util.Event(A<FsmEvent>.That.Matches(
receivedEvent => receivedEvent == fsmEvent
))
).MustHaveHappenedOnceExactly();
}
private void FireSignal(object signal) {
Container.Resolve<SignalBus>().Fire(signal);
}
private void SetupAction(SignalAction action) {
action.targetEvent = fsmEvent;
action.SetFsmUtil(util);
action.Init(Container);
action.Init(new FsmState(new Fsm()));
}
}
}
Und meine Datenquelle für den Test so:
using System.Collections;
using Core.Buildings;
using Game.FSM.SignalListener;
using Game.Signals;
using NUnit.Framework;
namespace Tests.UnitTests.Game.FSM.SignalListener {
public static class SignalListenerTestCases {
public static IEnumerable Signals() {
yield return new TestCaseData(new ExitBuildModeAction(), new ExitBuildModeSignal());
yield return new TestCaseData(new EnterBuildModeAction(), new EnterBuildModeSignal(BuildingType.Factory));
}
}
}
Sollte ich einen neuen Listener haben, dann brauch ich also in Zukunft nur eine Zeile Code hinzuzufügen um diesen zu testen - anstatt einen kompletten test aufzusetzen .