2

I am writing a small game library. I capsule the logic in my model named Block. My model Block communicates only via events to it's clients (Is this a good thing to do!?).

I wrote a simple unit test that tests some of that logic. It looks like this:

    [Test]
    public void CrossMissThrow()
    {
        UpdateEventArgs updateArgs = null;
        var block = new Block();
        block.Updated += (sender, args) =>
        {
            updateArgs = args;
        };

        block.NewGame();
        Assert.AreEqual(0, updateArgs.MissThrows);

        block.MissThrow();
        Assert.AreEqual(1, updateArgs.MissThrows);

        block.MissThrow();
        Assert.AreEqual(2, updateArgs.MissThrows);
    }

As you can see anytime I call a method of my model Block I will receive an update event from it. I immediately assert that some property of the EventArgs is correct.

This works and all tests pass, but: Can't I run into timing issues here? As I understand I call my method MissThrow() and assert immediately after that, i.e. after 1 ms. What if the method does take too long, let's say around 5 ms!? My event would be too late and my pass would fail.

Is my unit test okay like this or do I have to modify it because of that problem? Or do I even have to change my communication with the model?

Thanks in advance!

2
  • Unless you have asynchronous method all code will be executed on same thread in order: raise event, execute all event handlers, assert
    – Fabio
    Apr 29, 2018 at 15:51
  • 2
    To further clarify, events in .Net are synchronous. When you call handler?.Invoke() the runtime will execute each registered handler in turn before returning to the next line after handler?.Invoke.
    – RubberDuck
    Apr 30, 2018 at 22:16

1 Answer 1

2

As explained in the comments, events in .Net are synchronous, so with the system as shown in the question, there is no possibility of something going wrong. However, a more interesting question would be: how would you handle testing if the scenario was in fact asynchronous?

This is a scenario that often pops up in games whose game logic runs on a timer tick.

The idea is that instead of telling the game logic to perform an action (like block.MissThrow() in the original question) you post a message to the game logic telling it that the action should be performed at its earliest convenience. The message is stored in a queue, and the call to post the message returns immediately. (So, obviously, if you were to try and test for the reaction of the game logic immediately after posting the message, you would be out of luck, because the game logic would not have reacted yet.)

Then later, a timer event kicks in, (say, at the same frequency as the frame rate of the game,) at which point the game logic reads all messages from its queue, does whatever it needs to do, and issues notifications.

These notifications may be placed in other queues, or they may be delivered directly to their recipients, but in either case the notifications will be asynchronous since the issuing of the notifications happens as a result of responding to a timer event to read actions from a queue.

So, how would you test the game logic in such a scenario?

The answer is: by introducing an abstraction of the event system, so that we can have control over it, and then manipulating the event system from within the tests.

Here is how it would look like:

    EventSystem eventSystem = new EventSystem();
    GameLogic gameLogic = new GameLogic( eventSystem );
    List<UpdateEventArgs> updateEventArgs = new ArrayList<UpdateEventArgs>();
    gameLocic.Updated += (sender, args) => updateEventArgs.Add( args );

    gameLogic.postAction1();
    eventSystem.DoTick();
    Assert( updateEventArgs.Count == 1 );
    Assert( updateEventArgs[0].Equals(so-and-so) );
    updateEventArgs.Clear();

    gameLogic.postAction2();
    eventSystem.DoTick();
    Assert( updateEventArgs.Count == 1 );
    Assert( updateEventArgs[0].Equals(such-and-such) );
    updateEventArgs.Clear();

    gameLogic.postAction3();
    eventSystem.DoTick();
    Assert( updateEventArgs.Count == 1 );
    Assert( updateEventArgs[0].Equals(this-and-that) );
    updateEventArgs.Clear();

    ...

In an actual production runtime scenario, eventSystem.DoTick() would be invoked every 33 milliseconds for a rate of 30 frames per second, but in the tests we invoke it as we need it, so as to test the actions that are supposed to be performed by the game logic during the tick event.

The added benefit of doing things this way is that our tests run instantaneously, instead of having to wait several tens of milliseconds between each tick test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.