samedi 3 janvier 2015

Test helper for expected events sequence reporting duplicate events

I have a helper method for my unit tests that asserts that a specific sequence of events were raised in a specific order. The code is as follows:



public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction)
{
var expectedSequence = new Queue<int>();
for (int i = 0; i < subscribeActions.Count; i++)
{
expectedSequence.Enqueue(i);
}

ExpectEventSequence(subscribeActions, triggerAction, expectedSequence);
}

public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence)
{
var fired = new Queue<int>();
var actionsCount = subscribeActions.Count;

for(var i =0; i< actionsCount;i++)
{
subscription((o, e) =>
{
fired.Enqueue(i);
});
}

triggerAction();

var executionIndex = 0;

var inOrder = true;

foreach (var firedIndex in fired)
{

if (firedIndex != expectedSequence.Dequeue())
{
inOrder = false;
break;
}

executionIndex++;
}

if (subscribeActions.Count != fired.Count)
{
Assert.Fail("Not all events were fired.");
}

if (!inOrder)
{
Assert.Fail(string.Format(
CultureInfo.CurrentCulture,
"Events were not fired in the expected sequence from element {0}",
executionIndex));
}

}


Example usage is as follows:



[Test()]
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel)
{
var fuelTank = new FuelTank()
{
MaxFuel = maxFuel
};

var eventHandlerSequence = new Queue<Action<EventHandler>>();

eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x);

//Dealing with a subclass of EventHandler
eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e));

Test.ExpectEventSequence(eventHandlerSequence, () => fuelTank.FillFuel());
}


And the code under test:



public float Fuel
{
get
{
return fuel;
}
private set
{
var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel));

if (fuel != adjustedFuel)
{
var oldFuel = fuel;

fuel = adjustedFuel;

RaiseCheckFuelChangedEvents(oldFuel);
}
}
}

public void FillFuel()
{
Fuel = MaxFuel;
}

private void RaiseCheckFuelChangedEvents(float oldFuel)
{
FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel));

if (fuel == 0)
{
FuelEmpty.FireEvent(this, EventArgs.Empty);
}
else if (fuel == MaxFuel)
{
FuelFull.FireEvent(this, EventArgs.Empty);
}

if (oldFuel == 0 && Fuel != 0)
{
FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty);
}
else if (oldFuel == MaxFuel && Fuel != MaxFuel)
{
FuelNoLongerFull.FireEvent(this, EventArgs.Empty);
}
}


So the test expects FuelFilled to be fired before FuelChanged but in actuality FuelChanged is fired first, which fails the test.


However my test is instead reporting that FuelChanged is being fired twice, but when I step through the code it is clear that FuelFilled is fired after FuelChanged and FuelChanged is only fired once.


I assumed that it was something to do with the way lambdas work with local state, maybe the for loop iterator variable was only ever set to the final value, so I replaced the for loop with this:



var subscriptions = subscribeActions.ToList();

foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
fired.Enqueue(index);
});
}


However the result is the same, fired contains {1;1} instead of {1;0}.


Now I'm wondering if the same lambda is being assigned to both events instead of using the different subscription / index state. Any ideas?


Aucun commentaire:

Enregistrer un commentaire