views:

748

answers:

1

We've been using Flex for about 6 months here at work, and I found that my first batches of FlexUnit tests involving custom components would tend to follow this sort of pattern:

    import mx.core.Application;
    import mx.events.FlexEvent;
    import flexunit.framework.TestCase;

    public class CustomComponentTest extends TestCase {
        private var component:CustomComponent;

        public function testSomeAspect() : void {
            component = new CustomComponent();
            // set some properties...

            component.addEventListener(FlexEvent.CREATION_COMPLETE, 
                addAsync(verifySomeAspect, 5000));

            component.height = 0;
            component.width = 0;
            Application.application.addChild(component);
        }

        public function verifySomeAspect(event:FlexEvent) : void {
            // Assert some things about component...
        }

        override public function tearDown() : void {
            try {
                if (component) {
                    Application.application.removeChild(component);
                    component = null;
                }
            } catch (e:Error) {
                // ok to ignore
            }
        }

Basically, you need to make sure the component has been fully initialized before you can reliably verify anything about it, and in Flex this happens asynchronously after it has been added to the display list. So you need to setup a callback (using FlexUnit's addAsync function) to be notified when that's happened.

Lately i've been just manually calling the methods that the runtime would call for you in the necessary places, so now my tests tend to look more like this:

    import flexunit.framework.TestCase;

    public class CustomComponentTest extends TestCase {

        public function testSomeAspect() : void {
            var component:CustomComponent = new CustomComponent();
            component.initialize();
            // set some properties...
            component.validateProperties();

            // Assert some things about component...
        }

This is much easier to follow, but it kinda feels like I'm cheating a little either way. The first case is slamming it into the current Application (which would be the unit test runner shell app), and the latter isn't a "real" environment.

I was wondering how other people would handle this sort of situation?

+1  A: 

I see nothing wrong with using the async version. I can agree that the second version is shorter, but I'm not sure that I think it's easier to follow. The test does a lot of things that you wouldn't normally do, whereas the first example is more true to how you would use the component outside the test environment.

Also, in the second form you have to make sure that you do exactly what the framework would do, miss one step and your test isn't relevant, and each test must repeat this code. Seems to me it's better to test it in a situation that is as close to the real thing as possible.

You could have a look at dpUint's sequences, they made component testing a little more declarative:

public function testLogin():void {
    var passThroughData:Object = new Object();

    passThroughData.username = "myuser1";
    passThroughData.password = "somepsswd";

    var sequence:SequenceRunner = new SequenceRunner(this);

    sequence.addStep(new SequenceSetter(form.usernameTI, {text:passThroughData.username}));
    sequence.addStep(new SequenceWaiter(form.usernameTI, FlexEvent.VALUE_COMMIT, 100));

    sequence.addStep(new SequenceSetter(form.passwordTI, {text:passThroughData.password}));
    sequence.addStep(new SequenceWaiter(form.passwordTI, FlexEvent.VALUE_COMMIT, 100));

    sequence.addStep(new SequenceEventDispatcher(form.loginBtn, new MouseEvent("click", true, false)));
    sequence.addStep(new SequenceWaiter(form, "loginRequested", 100));

    sequence.addAssertHandler(handleLoginEvent, passThroughData);

    sequence.run();
}

(example from the dpUint wiki, see here for more info).

Theo