Monday, March 31, 2008

ASUnit, JUnit, XUnit u knu it

Alright. Bit of a lull in looking at updating my coding skills and knowledge. And recently, when an opportunity presented itself, I dove into Unit Testing. Before I jumped onto the Actionscript-based Unit testing bandwagon, I wanted to find out what unit testing was about. A few good references made the mud more clear.
Chapter 4 from Java Extreme Programming was very useful.
Wikipedia also had some good stuff to say.

Unit testing means testing through single assertions or units. The basic concept is that an assertion executes a conditional that either fails or passes a test. For example:

person=new Person("John","Smith");
assertTrue("this person first name is John: ","John" == person.fistName);
assertTrue("this person has a first name: ", person.hasFistName);

In this case, both assertTrue() method calls would return 'true' and the test would pass. This extends to other assertion assertXXX() methods that test for true and false results.
From this basic concept of unit tests emerges a whole methodology of writing software. Basically it means that a specification is clearly drawn up. What needs to happen in clearly stated in an unambiguous specification. Something that developers crave for at the beginning of the project anyway to avoid confusion and deliver on expectations. Based on this detailed specification, code is written which includes tests that verify this functionality only. So if I wanted to write a piece of code that drew a square on the screen, I would first write a test that would validate whether the square has been drawn correctly. Perhaps something like this:
square=new Shape("Square");
square.draw();
assertTrue("Square has finished drawing: ",square.isDrawn);

Now the assertTrue() method here would return a 'false' as I have not coded anything in the draw() method and the isDrawn getter only returns a false unless specific conditions in the Shape class are fulfilled, ie. the square has finished drawing.
I purposefully chose a shape draw type of test here because there is an additional factor to consider when it comes to unit testing. That is the timing of these tests. Now in this case I would have to base this assertion on some kind of an event that is fired when the square completes drawing because the draw() method is executed in the same block as the assertTrue() method, which will more than likely fail even if the square has been drawn correctly because of the time involved in drawing a square, especially if the square is drawn as an animation. All of this timing has to be handled outside of the unit tests themselves inside some sort of event handling framework. So I go ahead and write all of my code with these sorts of tests where needed, run the unit test and see that all of the tests have failed at this point. If they haven't then I've done something wrong as I haven't coded any functionality yet. Once all the tests failed, I can dive in and start coding, passing these tests as functionality is developed. Once I have coded everything and all of my tests are passing correctly, then I know I have fulfilled the specification requirements and am secure that my code is going to work, unless something breaks it, and what that is, I can easily follow while running the unit tests.
This level of confidence in the software structure then brings me to a phase where I can confidently take on the addition of new features and refactoring of old ones. Throughout this phase I will run the unit tests and see whether my changes have made any tests fail along the way. This is quite a comfortable spot to be in a as a developer because tests on my code are formalised and are runnable as changes occur. This may even eliminate the need for using a debugger to follow a rogue variable through to its untimely death.
So, fun fun. Downloaded and installed ASUnit, it ran like a dream. Now I have my security blanket. But as soon as I wrapped myself in it, I realised: What about UI testing? Unit testing only tests assertions in your code, but does not test the runtime functionality of the user interface. Being a flash developer, this became a major issue. After some digging around I found sparse information about how to reliably automate the testing of the user interface.Nothing really concrete. User Acceptance Testing came out as the most reliable way of testing the UI. I am looking for automated testing here so I thought I'd ave a think.
It may be possible to create a 'script' that used an event engine of sorts that ran through the application at runtime, pressing buttons and comparing resulting screens with expected ones. Some sort of bitmap comparison of a screen capture to a bitmap of a screen that has been determined as being the 'expected result'. The engine that does the button pressing on the screen could emulate a 'dumb user', one that presses on random buttons for no reason other than time wasting. It could also include some for of stress testing that presses buttons faster and faster in an attempt to crash the application. All these may be pipe dreams, but I can see the possibility of writing something like this. And it sounds like it would be fun too!


1 comment:

vonWolfehaus said...

Excellent post, thanks! I wasn't aware of how Unit Testing works, much less in AS3, so I'm going to give ASUnit a shot.

As for automated UI testing, sounds interesting, but I don't think it would be worth it. Not only does actual testing help find bugs, but it's eating your own dogfood, so to speak :) I find new workflows and enhancements I could make by simply running through my own interface.

OTOH, you could have a script trigger any MC with an event listener attached to it with some extra effort. That just sounds lazy in some twisted sort of way... or inefficient, at least.

Cheers.
// von