Unit testing Signal implementation
Robert Penner’s Signals has made my life so much easier these past few months. It avoids what I hate about Events while including features Events desperately need. There is one downside I have come across, which is mainly due to human error—making sure to dispatch a Signal with the correct value objects. If you set a Signal’s value classes like so:
public const timeChanged:Signal = new Signal(int, int, int); // hour, minute, second
…and dispatch it with incorrect value objects:
timeChanged.dispatch(1269874749); // unix timestamp
…Flash Builder throws an argument RTE. Since the RTE is thrown on call of dispatch and not at compile time, there is a possibility of missing the error, leaving your users with an unfortunate experience.
Now, this is easy to avoid by simply double checking your code, but if you’re working on a team, you can’t ensure this way that everything remains perfectly intact. You can, however, guarantee each signal fires correctly by unit testing. Sure, Robert has unit tested Signals to the max, but he hasn’t unit tested your implementation.
To do this, simply call dispatch of each Signal within a test method. The Signal will notify the runner if something is not right, indicating the line number and expected argument type as well. If you use a Signal bus to communicate throughout your app, your test will look something like this:
It’s that easy. Now you will know for sure if your Signal implementation is in tip top shape. All you need to do is run your unit test.
I’m interested to know what it is that you’re testing here.
My normal approach would be to test the action that triggers the dispatch. This involves 3 key bits of code:
1. A handler for the dispatch. This contains my assertions that the parameters fed through are what I expected them to be. This is a standalone function in my testcase. I might be able to actually test the values, or I might only be able to confirm the types, depending on whether the dispatch varies.
Then I write the actual test, which has in it:
2. Code adding the handler as an async expectation with a timeout of 50ms. (So if the dispatch is never fired I get an error).
3. Code running the function that should trigger the test – eg testInstance.doThingThatShouldDispatchStuff();
What you’ve written is a lot more efficient! But… does it really test that you have remembered to dispatch all your arguments in the doThingThatShouldDispatchStuff function?
Could be that I’m testing more than you’re looking for – I have learned from experience that there is no code too simple for me to screw up
@Stray — I’m testing that the dispatched arguments (or value objects) match the value classes that are assigned in the Signal constructor. There have been a number of occasions when I, or someone else, changed one of the value classes and forgot to change the dispatched value object. If that Signal is dispatched deep in the site, you might miss it, and then there’s an RTE just waiting to happen. I wish it were as easy as right-click refactor value class, but that’s one of the risks with Signals.
Now, one thing this test doesn’t cover is your handlers, which run the risk of being mismatched too. In that case, you’d just test the handler in its class’s test case.
Ah gotcha – so you’re verifying the creation of the Signal, but not the dispatch?
So it’s a kind of early warning system rather than a test of the dispatches themselves? That makes sense. Especially where you’re using a signal bus to share signals around. Do you test the dispatches separately as well?
@Stray — Correct. In all honesty, I don’t unit test as much as I should. :/ I typically unit test my core and utility classes, but when it comes to the app itself, I get lost on how to test it all. After seeing the video of your 900+ successful test run, I’m sure you have the answer
I’ll have to designate a weekend to figuring it all out.
Testing is a dark art all of its own. My temptation is to test the things that are easy to test and not bother with the things that are trickier – that’s one trap. Developing roboteyes and having mockolate (Drew Bourne’s genius mocking toolkit) sussed out has helped a lot with that.
The other, bigger trap that I can fall into is to not test the things that are soooo simple that we couldn’t possibly mess them up – like getter / setters and so on. The more I test, the more experiences I have of realising I’m not quite as smart as I think I am.
My favourite screw up is to write a function but never call it, and then wonder why the app didn’t do what I expected. Testing saves my ass there… if I remember to write the test – which is why I have to do TDD, because that way at least it’s noisy if I forget to write the code!
The early warning system you’ve created above is perfect though – because if that test fails you know there’s only one possible cause: someone changed the parameters in the Signal. We all know that writing code is quick – look how fast I’ve typed this comment! It’s diagnosing problems that’s the tough part, and your code above is a really, really tight diagnostic. Cool.