You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Current »

Assumptions

This page assumes that you are already familiar with writing basic test cases and asynchronous testing, as well as familiarity with [Before] and [After] methods. Please follow the links provided if you do not feel comfortable with these concepts.

Why Use UIImpersonator?

Sometimes in Flex, functionality in a class will not execute until the class has been added to the display list. This occurs frequently with custom components in the Flex framework (ex: A custom Button). The org.fluint.uiImpersonation.UIImpersonator class allows you to test functionality that '''requires''' a class to be added to the display list in order for the functionality to occur by providing a public static interface for adding, removing, and maintaining children on the display list within the FlexUnit 4.x environment.

UIImpersonator Defined

UIImpersonator is a class that defines a set of static methods and properties which parallel all of the standard child-related properties of UIComponent and Container These are:

  • addChild()
  • addChildAt()
  • removeChild()
  • removeChildAt()
  • removeAllChildren()
  • getChildAt()
  • getChildByName()
  • getChildIndex()
  • setChildIndex()
  • get numChildren()

By making these methods static and globally available, any test or test-related bit of code may access these methods at any time. In other words, UIImpersonator does what its name suggests: It impersonates standard functionality for adding/removing components to/from the UI (display list).

In order to take advantage of the UIImpersonator, two metadata attributes need to be added: async and ui. The async attribute lets FlexUnit know that the method in question is asynchronous, that is, that we cannot assume the method is done with everything simply because the method itself has completed execution. Waiting on a FlexEvent.CREATION_COMPLETE, or Event.ADDED for pure AS3, will be the most common use-case of asynchronous behavior when dealing with UIImpersonator. The ui attribute lets FlexUnit know that you will be interacting with the display list and is necessary for implementing UIImpersonator.
:-Note: While async is not ''necessarily'' required to implement UIImpersonator, in ''most'' cases, if you need something to be on the display list, you also need asynchronous testing.

'''NOTE''': A test using UIImpersonator probably does not meet the strict definition of a Unit Test since it relies on several classes interacting in order to test the functionality, meaning all of the reliant functionality is already presupposed to be working properly in the test scenario. This is what is called an "integration test." For more, see the Wikepedia entries on Integration Testing and Unit Testing.

An Example Use Case of UIImpersonator

Let's say you have created a custom Button component called MyButton that adds functionality based on a new style, myStyle. Without worrying about what this style does, we may want to test that the default value of this style is getting applied appropriately. Unfortunately, because of the way styles work in Flex, we can only verify this '''after''' the component has been added to the display list. In order to do this, we use UIImpersonator so that we can:

:1) Add the custom button to the display list.
:2) Wait for a "creationComplete" event to ensure the component has finished its initial life cycle.
:3) (Finally) Verify that the style is the expected style.

Below is a code example of this:


protected var myButton : MyButton;

[Before(async,ui)]
public function setUp() : void
{
    myButton = new MyButton();
    Async.proceedOnEvent( this, myButton, FlexEvent.CREATION_COMPLETE, 100 );
    UIImpersonator.addChild( myButton );
}
        
[After(async,ui)]
public function tearDown():void
{
    UIImpersonator.removeChild( myButton );         
    myButton = null;
}

[Test(async,ui,description="Tests default value of 'myStyle' style on 'MyButton' class")]
public function myButton_myStyle_defaultValue() : void
{
    Assert.assertEquals( myButton.getStyle( "myStyle" ), "expectedDefaultValue" );
}

You may notice that this test scenario makes use of [Before] and [After] to add and remove the MyButton instance to the display list. The reason for this is threefold:

  • It is easier to write the test this way. If we were to do the adding/removing in the test case itself, we would need to add various asynchronous event listeners and event need to move the success criteria ''out of'' the test case itself and ''into'' an event listener instead. For more on this, compare the two walk-throughs Writing an Asynchronous Test vs. Using Asynchronous Startup.
  • It is easier to read. We have just effectively created a test that '''only contains functionality that we are testing'''. Since we are ''presupposing'' adding and removing the custom button to the display list works and not actually ''testing'' it, the test very clearly shows what is being tested without adding code that isn't being tested.
  • It is easier to reuse. By using [Before] and [After] methods, now we have a test case ready to test ''any'' functionality on the MyButton class that needs it to be added to the display list. For example, we may want this test case to test setting the custom style, or test expected behavior if the style is undefined, etc. All we would have to do is add the additional tests without having to worry about re-using the UIImpersonator, since the [Before]/[After] will do this automatically.

In other words, although you don't ''have to'' use [Before] and [After], it is generally a good practice.

Why Use UIImpersonator? - Part 2

You might still be asking yourself, "Why use UIImpersonator? Why not just add the component to FlexGlobals.topLevelApplication in Flex 4.x or Application.application in Flex 3.x?"

The reason UIImpersonator is important is that children added to it, as opposed to the afore-mentioned globally-accessible components, are '''not affected by customization to the test environment itself'''. A clear example of this is styles: If the FlexUnit test environment has applied any type-selector CSS styles, you may not be testing in the environment in which you expected. UIImpersonator, in other words, ''ensures'' your test environment is completely separate from the FlexUnit application environment.

In addition, if you ''have'' tried writing tests by directly adding components to the display list, you may have noticed that those components will actually show up in the FlexUnit application itself, which, at the very least, looks a bit sloppy from a design perspective. UIImpersonator keeps this from happening by essentially "hiding" the components from the actual, visible display.

  • No labels