Unit Testing: IoC/DI, Robotlegs and FlexUnit 4

I’ve been reviewing the various IoC containers available for Flex/Actionscript. One of the benefits of IoC and DI is that it greatly facilitates unit testing. By injecting our dependencies into our applications actors, we are effectively isolating them from the other classes that make the application function. “So what?”

In computer programming, unit testing is a software verification and validation method in which a programmer tests that individual units of source code are fit for use. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual program, function, procedure, etc., while in object-oriented programming, the smallest unit is a class, which may belong to a base/super class, abstract class or derived/child class.

Ideally, each test case is independent from the others: substitutes like method stubs, mock objects, fakes and test harnesses can be used to assist testing a module in isolation. Unit tests are typically written and run by software developers to ensure that code meets its design and behaves as intended. Its implementation can vary from being very manual (pencil and paper) to being formalized as part of build automation. -wikipedia unit testing entry

To effectively unit test classes, we want them to be as isolated as possible. The class needs to stand on its own and have its functionality vetted to ensure that it behaves as expected. When we start to test how our classes behave together, we have entered the land of integration testing. Integration testing is a worthwhile pursuit also, but before we get to that point, we really want to ensure that our classes are solid by themselves.

Using the example in this Robotlegs Image Gallery demonstration, we are going to test a couple of the classes that make up the application. The demo isn’t complex. It has a very simple model, a single service, and just a few views. In fact, it might even seem like a waste of time to unit test a simple example such as this, but the concepts are applicable to larger applications where the benefits of this type of testing really start to pay off.

FlexUnit 4 is the framework that is used here. It provides very handy asynchronous testing, meta data support, and a whole bucket of stellar features that I won’t even begin to scratch the surface of. FlexUnit 4 also comes with a graphical testrunner interface:

success

On to the code…

full source is here…

This test runner interface is added to the RobotlegsImageGalleryTestRunner.mxml application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="utf-8"?>
<s:Application
	xmlns:fx="https://ns.adobe.com/mxml/2009"
	xmlns:s="library://ns.adobe.com/flex/spark"
	xmlns:mx="library://ns.adobe.com/flex/halo"
	xmlns:flexUnitUIRunner="https://www.adobe.com/2009/flexUnitUIRunner"
	creationComplete="onCreationComplete()">
	<s:layout>
		<s:BasicLayout/>
	</s:layout>
	<fx:Script>
		<![CDATA[
			import org.robotlegs.demos.imagegallery.test.suites.RobotlegsImageGalleryTestSuite;
			import org.flexunit.listeners.UIListener;
			import org.flexunit.runner.FlexUnitCore;
 
			private var core:FlexUnitCore;
			private function onCreationComplete():void
			{
				this.core = new FlexUnitCore();
				core.addListener( new UIListener( uiListener ));
				core.run( RobotlegsImageGalleryTestSuite )
			}
		]]>
	</fx:Script>
 
 
	<flexUnitUIRunner:TestRunnerBase id="uiListener" width="100%" height="100%"/>
</s:Application>

The core runs our test suites, in this case we are only running a single suite: the RobotlegsImageGalleryTestSuite. The test suite carries metadata that identifies it as a FlexUnit suite. There is no need to extend TestSuite as in previous version of FlexUnit:

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.robotlegs.demos.imagegallery.test.suites
{
	import org.robotlegs.demos.imagegallery.test.cases.TestFlickrService;
	import org.robotlegs.demos.imagegallery.test.cases.TestGalleryProxy;
 
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class RobotlegsImageGalleryTestSuite
	{
		public var testFlickrService:TestFlickrService;
		public var testGalleryProxy:TestGalleryProxy;
	}
}

The suite further defines the test cases that we are going to run, TestFlickrService and TestGalleryProxy. Any other test cases would also be included in this suite. TestFlickrService extends the Robotlegs Service class. It makes an asyncronous call to the Flickr API and retrieves images via the ‘interestingness’ category, as well as through a text search. The Service class has a dependency that is injected by the framework on its eventBroadcaster property. EventBroadcaster simply wraps an event dispatcher to provide a simplified interface for the framework to access. Service also provides a dispatch(event) convenience method for sending event notifications to the framework context. Our tests are not run within this context, however, so we have run into a problem. Since the framework injects the EventDispatcher into the EventBroadcaster automatically, we apparently have no way to access it and we can’t receive framework notifications.

As it happens, we can simply inject our dependencies manually. The TestFlickrService case has a serviceDispatcher property that extends EventDispatcher. In our setUp() method that is called prior to every test, we create a new EventBroadcaster (which takes our serviceDispatcher as an argument) and add it to our fresh FlickrImageService instance. Now the test case has access to the injected dispatcher, and we can listen for our normal flash events that Robotlegs uses for notifications.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package org.robotlegs.demos.imagegallery.test.cases
{
	import flash.events.EventDispatcher;
 
	import org.flexunit.Assert;
	import org.flexunit.async.Async;
	import org.robotlegs.demos.imagegallery.events.GalleryEvent;
	import org.robotlegs.demos.imagegallery.remote.services.FlickrImageService;
	import org.robotlegs.mvcs.EventBroadcaster;
 
	public class TestFlickrService
	{
		private var service:FlickrImageService;
		private var serviceDispatcher:EventDispatcher = new EventDispatcher();
 
		[Before]
		public function setUp():void
		{
			serviceDispatcher = new EventDispatcher();
			service = new FlickrImageService();
			service.eventBroadcaster = new EventBroadcaster(this.serviceDispatcher);;
		}
 
		[After]
		public function tearDown():void
		{
			this.serviceDispatcher = null;
			this.service.eventBroadcaster = null;
			this.service = null;
		}
 
		[Test(async)]
		public function testRetreiveImages():void
		{
			this.serviceDispatcher.addEventListener( GalleryEvent.GALLERY_LOADED, Async.asyncHandler(this, handleImagesReceived, 3000, null, handleServiceTimeout), false, 0, true);
			this.service.loadGallery();
		}
 
		[Test(async)]
		public function testSearchImages():void
		{
			this.serviceDispatcher.addEventListener( GalleryEvent.GALLERY_LOADED, Async.asyncHandler(this, handleImagesReceived, 3000, null, handleServiceTimeout), false, 0, true);
			this.service.search("robotlegs");
		}
 
		protected function handleServiceTimeout( object:Object ):void
		{
	    	        Assert.fail('Pending Event Never Occurred');
		}
 
		protected function handleImagesReceived(event:GalleryEvent, object:Object):void
		{
			Assert.assertEquals("The gallery should have some photos: ", event.gallery.photos.length > 0, true)
		}
 
 
	}
}

After each test is run, our tearDown method is called by FlexUnit which sets our service and its eventBroadcaster to null in preparation for the next test in this case to be run. While this example isn’t very complex, it is easy to see how this could be extended to a much more complicated service.

The GalleryProxy class is tested in much the same way, though it doesn’t have any async methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.robotlegs.demos.imagegallery.test.cases
{
	import org.flexunit.Assert;
	import org.robotlegs.demos.imagegallery.models.proxies.GalleryProxy;
	import org.robotlegs.demos.imagegallery.models.vo.Gallery;
	import org.robotlegs.demos.imagegallery.models.vo.GalleryImage;
 
	public class TestGalleryProxy
	{
		private var galleryProxy:GalleryProxy;
 
		[Before]
		public function setUp():void
		{
			this.galleryProxy = new GalleryProxy()
		}
 
		[After]
		public function tearDown():void
		{
			this.galleryProxy = null;
		}
 
		[Test]
		public function testSetGallery():void
		{
			var gallery:Gallery = new Gallery();
			this.galleryProxy.gallery = gallery;
			Assert.assertEquals("GalleryProxy should have a gallery", this.galleryProxy.gallery != null, true );
		}
 
		[Test]
		public function testSetSelectedImage():void
		{
			var image1:GalleryImage = new GalleryImage()
			var image2:GalleryImage = new GalleryImage()
			var gallery:Gallery = new Gallery()
			this.galleryProxy.gallery = gallery;
			gallery.photos.addItem(image1);
			gallery.photos.addItem(image2);
			image1.selected = false;
			image2.selected = true;
			this.galleryProxy.setSelectedImage(image1);
			Assert.assertEquals("Image1 should be selected", image1.selected, true);
			Assert.assertEquals("Image 2 should NOT be selected", image2.selected, false);
		}
	}
}

With these simple tests we are 100% certain that our classes function as expected on their own, completely isolated from the rest of the application.

You really should call your mom…

We are all unit testing our code and following strong Agile/TDD processes, right…. right? I don’t know how much unit testing is going on in the Flash/Flex world, but my guess that the overall test coverage percentage is very small. RIAs are here. They are becoming full fledged applications that people rely on to do business. There will come a time in your RIAs life that you will need to get in there and do some refactoring. It might be for performance, fixing bugs, or adding some new kickass feature to push your app to the next level. Fact, without unit testing:

you’re not refactoring; you’re just changing shit. -Hamlet D’Arcy

There are many, many arguments for unit testing your code, but for me this is the most compelling. At some point in the future, you are going to want to get in there and make some changes. If your application is important to your users, you want to make sure those changes don’t break everything. It is guaranteed that they will break something, the question is will you know immediately what happened, or at least where to look?

We are getting the proper tools for the job. FlexUnit 4 is an incredible project. There is a robust selection of frameworks that all have merits and can facilitate structured, well designed applications. I’m personally out of excuses. I want to develop applications that my users can trust and rely on to do business, and unit testing is a part of that recipe.

  • https://shaun.boyblack.co.za/blog/ shaun

    Nice one Joel!

    As the EventBroadcaster is a simple wrapper around the EventDispatcher, nulling and re-instantiating the broadcaster doesn't do much – ie, the EventDispatcher it wraps stays alive between tests, and the registered listeners will survive. I would recommend re-instantiating the EventDispatcher between tests. Something like:


    private var service:FlickrImageService;
    private var serviceDispatcher:EventDispatcher;

    [Before]
    public function setUp():void
    {
    serviceDispatcher = new EventDispatcher();
    service = new FlickrImageService();
    service.eventBroadcaster = new EventBroadcaster(serviceDispatcher);
    }

    [After]
    public function tearDown():void
    {
    service.eventBroadcaster = null;
    serviceDispatcher = null;
    service = null;
    }

    Not sure if that will mess about with the async stuff – but I don't think so.

  • https://shaun.boyblack.co.za/blog/ shaun

    Nice one Joel!

    As the EventBroadcaster is a simple wrapper around the EventDispatcher, nulling and re-instantiating the broadcaster doesn't do much – ie, the EventDispatcher it wraps stays alive between tests, and the registered listeners will survive. I would recommend re-instantiating the EventDispatcher between tests. Something like:


    private var service:FlickrImageService;
    private var serviceDispatcher:EventDispatcher;

    [Before]
    public function setUp():void
    {
    serviceDispatcher = new EventDispatcher();
    service = new FlickrImageService();
    service.eventBroadcaster = new EventBroadcaster(serviceDispatcher);
    }

    [After]
    public function tearDown():void
    {
    service.eventBroadcaster = null;
    serviceDispatcher = null;
    service = null;
    }

    Not sure if that will mess about with the async stuff – but I don't think so.

  • https://compactcode.com/ Shanon

    I'm a little late to the party but nice post. I've just started blogging on the topic of flex unit testing myself. The Flex community seems to be a way behind others in terms of knowledge and application of unit testing techniques.

  • https://compactcode.com/ Shanon

    I'm a little late to the party but nice post. I've just started blogging on the topic of flex unit testing myself. The Flex community seems to be a way behind others in terms of knowledge and application of unit testing techniques.

  • https://frgmntd.livejournal.com/ Nek

    That's nice but what about Mediators testing or anything else with [Inject]?
    Or being more specific what is the best way to deal with dependencies of testee classes?
    update: Trying ASMock. Looks nice so far.

  • https://joelhooks.com Joel Hooks

    When testing classes that contain [Inject]-able properties, you manually inject the values in a test as needed. This allows you to substitute test implementations. When testing, we aren't trying to see if the automated DI works, we want to know the classes work independent of any external class, framework, or service.

  • https://frgmntd.livejournal.com/ Nek

    Flex 3 version of main.xml:

    <?xml version=”1.0″ encoding=”utf-8″?>
    <mx:Application
    xmlns:mx=”https://www.adobe.com/2006/mxml”
    xmlns:flexunit=”flexunit.flexui.*”
    xmlns:flexUnitUIRunner=”https://www.adobe.com/2009/flexUnitUIRunner”
    creationComplete=”onCreationComplete()”
    >
    <mx:Script>
    <![CDATA[
    import org.robotlegs.demos.imagegallery.test.suites.RobotlegsImageGalleryTestSuite;

    import org.flexunit.listeners.UIListener;
    import org.flexunit.runner.FlexUnitCore;

    private var core:FlexUnitCore;
    private function onCreationComplete():void
    {
    this.core = new FlexUnitCore();
    core.addListener( new UIListener( uiListener ));
    core.run( RobotlegsImageGalleryTestSuite )
    }
    ]]>
    </mx:Script>
    <flexUnitUIRunner:TestRunnerBase id=”uiListener” width=”100%” height=”100%”/>
    </mx:Application>

  • https://frgmntd.livejournal.com/ Nek

    That's nice but what about Mediators testing or anything else with [Inject]?
    Or being more specific what is the best way to deal with dependencies of testee classes?
    update: Trying ASMock. Looks nice so far.

  • https://joelhooks.com Joel Hooks

    When testing classes that contain [Inject]-able properties, you manually inject the values in a test as needed. This allows you to substitute test implementations. When testing, we aren't trying to see if the automated DI works, we want to know the classes work independent of any external class, framework, or service.

  • https://frgmntd.livejournal.com/ Nek

    Flex 3 version of main.xml:

    <?xml version=”1.0″ encoding=”utf-8″?>
    <mx:Application
    xmlns:mx=”https://www.adobe.com/2006/mxml”
    xmlns:flexunit=”flexunit.flexui.*”
    xmlns:flexUnitUIRunner=”https://www.adobe.com/2009/flexUnitUIRunner”
    creationComplete=”onCreationComplete()”
    >
    <mx:Script>
    <![CDATA[
    import org.robotlegs.demos.imagegallery.test.suites.RobotlegsImageGalleryTestSuite;

    import org.flexunit.listeners.UIListener;
    import org.flexunit.runner.FlexUnitCore;

    private var core:FlexUnitCore;
    private function onCreationComplete():void
    {
    this.core = new FlexUnitCore();
    core.addListener( new UIListener( uiListener ));
    core.run( RobotlegsImageGalleryTestSuite )
    }
    ]]>
    </mx:Script>
    <flexUnitUIRunner:TestRunnerBase id=”uiListener” width=”100%” height=”100%”/>
    </mx:Application>

  • totty

    But what if I have data from server, then user chooses some of it, and then I want to test on it?

  • Pavel Krusek

    Hi Joel!

    Thanks for nice article about unit testing with RL.
    But what about async testing with Signals?
    I found an interesting utility – https://github.com/eidiot/as3-…

    Thanks,

    Pavel