Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Saturday, April 16, 2016

Darren on the JVM: Intro to TDD Part 1: Some Ideas, Principles & Techniques

PROLOGUE

A while ago now I applied for a Java role at a certain newspaper,
having heard they were Agile and proponents of TDD. After a successful
initial telephone screening interview, I was issued a programming
exercise - Battleships! These exercises serve to reveal a candidates ability to

  • Digest a problem description
  • Extract requirements and define scope
  • Identify expected program behaviour
  • but above all else… WRITE CODE!

In my case, I was given the opportunity to demonstrate my aptitude for TDD, as their required approach to every-day programming


SPOILER: I did not get the job :(

There is a some personal irony in this. Also I was unfortunately not given any constructive feedback either.
TDD to some maybe old-hat or second-nature, but it is pervasive and still very much a component of many interview processes - which many, many candidates still fail - reflecting it is still a concern for hiring teams.

In this short series of posts hopefully I can help those newcomers ramp-up and secure those much desired jobs and to perhaps offer those existing TDD’ers and indeed TDD-interviewers some added perspective.

In this blog I will demonstrate how I do Test Driven Development, with an approach - I call it ‘The Introspective Approach’ - that helps me get going when I get stuck.

In subsequent posts in the series I will use a variant of the Battleships game, run from the command-line, as the context for this learning exercise.
Battleships provides a good context as the game is quite familiar and, from a programming point of view, the inputs are easy to define and outputs easy to capture.

The complete exercise spec, along with the solution code will be available on GitHub; the code excerpts wherever included will link back to the relevant file and version there.

First the Fundamentals

Let’s boil down what testing and programming in general is; I offer:

A program is just a function over some data; Given some input, a program will deterministically produce some output.

I say ‘deterministic’ as computers in general are well-behaved and we have dominion over all inputs, so basically the same inputs will produce the same outputs, ALWAYS!
~… although, what we don’t know may… segmentation fault, NPE, BSD :D #geek-humour~

Testing is merely defining the behaviour of a program - by stating your expectations for its outputs given a set of inputs - and verifying this behaviour holds true. The task then is to state the minimal set of expectations representative of all realistic input-output pairs, in the context of the business-domain or production-environment.

Thus programming is the task of implementing the most concise yet general solution, for which that expected behaviour is verified.

Some Informal Guidelines… Techniques within Techniques

When TDD’ing, I use a few techniques to keep me on track and help me decide what to do next; these techniques are:

  • Defer-and-Delegate
  • Mock
  • Expand-to-Contract (Precursor to Refactor)
  • Refactor

The order isn’t meant to be prescriptive, but if you have a go you’ll see it emerges naturally; let’s look at how these techniques are related and facilitate one another.

Defer and Delegate

Typically a program has a single point of entry e.g. like the public static void main(String[] args) method, but this seldom is the best place to contain all the application logic or make all the decisions.

At the crux of it is the Single Responsibility principle

This technique advocates breaking down the program input and thus breaking down the problem space. If you defer any decision making over any part of the program input as late as possible, ironically this helps you to progress with the solution.

When delegating as well, you decompose the problem and decision making and push those responsibilities off to other components; you end up with a bunch of classes collaborating - some orchestrating, some decision making, some consensus building - to form the system.

If you focus on testing one part of the system, it’s one decision or responsibility, then you can fix the behaviour of its collaborators to how you need them, in order to support implementing that decision logic or fulfilling that responsibility.

Of course, this ‘fixing of behaviour’ is mocking.

Mock

For this exercise I’ve used Mockito and ScalaMock, but in general a mocking library allows you to mock or stub at least the public API of a component.

This advocates the Design by Interface technique, which in turn supports/facilitates the High Cohesion, Low Coupling and Interface Segregation principles.

If we delegate certain decision making to component dependencies, for each we define a public API with which to interact with that component dependency and that public API can be mocked.

We know the part of the input that must be submitted to the component and we know what we need back - mocking allows us to install that behaviour for some or all of the duration of the test. Furthermore we can specify only that input will produce that particular output or we can fix it so that the output we want is always produce, regardless of input.

Mocking provides quite a lot of flexibility like that, but it’s best not to get too carried away.
Generally it’s not good to recursively mock mocks returning other mocks. That said, mocking factories - as you will see in the code - solves the problem of not being able to instantiate an interface, where they do not have implementations or constructors to call.

Expand-to-Contract

A former boss once taught me this one; as an Agile coach and mentor, he observed me trying to go from no-code straight to the perfect-code. I have since observed many colleagues and clients do the same thing, that is:

  1. Assume omniscience
  2. Think ourselves super clever and don’t need to go through the motions
  3. By pure genius we can just materialise that perfect-code at the first attempt, BOOOM!

Nah! We are mere mortals - the compiler and tests will never let us forget this :D

To get an understanding of what we are doing, we should first write code objectively (read: with focus), letting if-else and switch statements expand and for- and while-loops nest as deep as needed, duplicating freely to create a first-cut solution that works; this may seem counter intuitive and look a little silly, but hopefully it makes the problem space easier to understand and reveals whether you have covered all the bases.

I want to say the trick here is to make duplication really big and obvious.

Once it’s working, THEN refactor! AND don’t forget to revisit the nested loops and review their performance.

Refactor

Refactoring is more an activity than a technique; there are a tonne of books on the topic and I won’t delve too deep into it here. I’ll just propose refactoring serves to simplify the code, make it more concise, readable, maintainable, debuggable - simple!

One of the main techniques for refactoring is to hunt down and eliminate code duplication; this technique transcends paradigm boundaries (i.e. OO, to functional, to procedural, etc) and is particularly relevant for testing as…

Every line of code written is a line of code that must be tested

In other words, make less work for yourself and reduce you maintenance and testing overhead.

So those are the techniques; nothing really new, yet quite common to not see them done ;P
Also, if you’ve ever heard someone mention the term ‘Design by Testing’, these techniques are what they were talking about… or the outcome of these techniques, anyway.

The Introspective Approach

In my early years of practicing Agile and TDD specifically, I would often struggle to find a starting point.

You may, for instance be given a grand vision for some system or a solution or get a bright idea, but some how you have to bridge the gap between that vision and your first TDD unit-test. As always when trying to achieve something, the first step is always the hardest! So I started using this Introspective Approach to overcome this inertia and get the TDD-ball rolling.

Essentially you defer defining the system-, component-, class-, object- or subject-under-test and sort-of start in the first-person as the test itself and let the implementation develop and emerge from there.

You start with the highest level requirement and the highest level expectation i.e. the program input and the program output. Then define a single public method to consume that input and produce that output.

If designed well, any kind of test should be a black-box test, with which in this context public methods are the only means of interaction - this should always give us 100% coverage over any private methods

Next Up

As mentioned at the start, the follow-up to this post in the series are:

Intro to TDD Part 2: Battleships, Java-style
A walkthrough of these techniques with concrete examples in Java
Intro to TDD Part 3: Battleships, Scala-style
A walkthrough of these techniques with concrete examples in Scala.

Again, the context for both walk-throughs is the Battleships game, which will be specified by a few example scenarios each containing input data and expected output results.

As of this writing, the implementations for both are complete and the posts will (read: should) follow shortly.

Stay tuned!

Thursday, April 16, 2015

Darren on the JVM: Java and Scala with IntelliJ's Compile Server

PROLOGUE

I went searching YouTrack, doing my due diligence, for an issue (described below) before raising a feature request for IntelliJ's Compile Server facility.

I came across this issue, which describes slow compilation despite having the compile server enabled.

I echo here my comments and findings

If you are trying to make use of IntelliJ's Compile Server facility, say, to make you Red, Green... Refactor cycle go really fast, but can't seem to make it work or observe any speed gains, you may have overlooked an important step.

If when you make a change and click Run or press Shift+F10 and IntelliJ proceeds to 'make' your project, this applies to you.

The problem isn't to do how the Compile Server is configured; it has everything to do with your run configurations.

When you right-click a test or 'main' class, amongst other things, you have the option to 'Create Run Configuration' or 'Run' or 'Debug'; the first option creates a run configuration and pops-up a form to make changes before saving it (does not actually run it), where as the last two create run configurations for you implicitly, which IntelliJ reuses if you attempt to re-run/debug the same package/class/method element again later.

In ALL cases, not just these three, run configurations are created from a list of default configurations. The default for these three cases and many others, include the 'make' Before launch task.

This can be easily verified by playing around with the defaults and making implicit run configurations by right-clicking stuff as per normal.

But to address the slow compilation:

  1. Modify the default templates for the run configurations of interest - I changed Application, JUnit Test, Scala Console, ScalaTest and Spec2 - and removed the 'make' entry in the 'Before launch' section.
  2. Edit your existing run/test configurations, also removing the 'make' 'Before launch' section.

Unless you explicitly change your configuration defaults, right clicking on a test and selecting run will always generate a configuration with the 'make' task present.

Having just read all the blog posts from IntelliJ regarding the Compile Server and SBT's source-base change analysis, they clearly forgot to mention this important step.

Once removed, you can see when clicking Run the test runner starts spinning up immediately (assuming you're trying to run tests).

The only issue I have seen here (I have searched YouTrack for a mention of it to no avail) is if you make a change and hit Run or Shift+F10 immediately, the test runner might fail as the test class is momentarily not on the classpath, that is, in that brief window between detecting the change and the compile server doing its thing.

However, clicking Run (Shift+F10) normally succeeds on the second attempt and it is still infinitely faster than waiting for a needless make to complete.

Now my IntelliJ seems truly amazing

Wednesday, November 19, 2014

Darren on Flex: FlexUnit4 Testing with Parsley - Testing with Mock Injections

In Part-5 I showed how to tidy away all (most of) the Parsley Messaging and Flex event management boiler-plate by extending the Fluint-aware DSL introduced in Part-3, which made tests Parsley-aware.

This much overdue final part of the FlexUnit4 Testing with Parsley series demonstrates mock injection and leverages Mockito-Flex to achieve that.
(I will give a shout to the team behind Mockolate, as that is also a very good mocking library)

Mockito-Flex utilizes ASMock internally and I encountered a problem where the application-domain used by ASMock for creating mocks, differed from that used by Parsley for its managed context environment.
I managed to clone and patch the Mockito-Flex codebase (pull-request imminent) such that in this patched version, Mockito-Flex specifies to ASMock what application domain to use. Awesome!

The 'test' code re-re-re-re-revisted...

Mock Injection
Previously, a stubbed service implementation was used to provide the behaviour needed for the successful running of the tests.
The smell that comes with using stubs is that they tend to introduce test logic, as was the case here, which is:
  1. Only implicitly tested, by default of being used in a test and...
  2. Only ever used through test code and not main/business code, so does not really represent any business value
Furthermore, this test logic resides in a different class to the test making it harder to understand what is going on (or harder to debug it if/when something goes wrong).

We can rid ourselves of these code-smells by using mocking; the DSL provided by Mockito-Flex allows us to declaritively specify how the mocks in our Parsley context must behave AND we do that in-line with our tests.

With the mocking and Parsley-aware DSLs combined, we still get to write easy-to-read story-style tests i.e. given-when-then, like we did before.

DSL(Mocking + Parsley) - Inheritance = A New Approach
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.GetPersonCommandMockedComponentTestContext;
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.create;
    import com.darrenbishop.support.flexunit.parsley.ParsleyDslRule;
    import com.darrenbishop.support.flexunit.parsley.dsl.*;

    import flash.events.ErrorEvent;

    import org.hamcrest.assertThat;
    import org.hamcrest.object.equalTo;
    import org.mockito.integrations.flexunit4.MockitoRule;
    import org.mockito.integrations.given;

    public class GetPersonCommandMockedParsleyRuleDSLComponentTest {

        [Rule(order=1)]
        public var mockito:MockitoRule = new MockitoRule();
        
        [Rule(order=2)]
        public var parsley:ParsleyDslRule = new ParsleyDslRule(GetPersonCommandMockedComponentTestContext);
        
        [Mock]
        public var service:DirectoryService;
        
        [Inject]
        public var command:GetPersonCommand;
        
        public var dodgyPerson:Person;
        
        public var soundPerson:Person;
        
        [Before]
        public function prepareEntities():void {
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            given(service.lookupById(soundPerson.id)).will(returnEventually(soundPerson));

            dispatchMessage(PersonEvent.newLookup(soundPerson.id));
            
            waitForMessage(PersonEvent.FOUND);
            then(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
        }

        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            given(service.lookupById(dodgyPerson.id)).will(returnEventually(soundPerson));

            dispatchMessage(PersonEvent.newLookup(dodgyPerson.id));
            
            waitForError(ErrorEvent.ERROR);
            then(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(fmt('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
        }
    }
}

The first thing to notice is the test-case no-longer needs to inherit from the ParsleyHelper class; all that DSL goodness is made available in standalone functions, in much the same way as those provided in the org.hamcrest.*, org.mockito.integrations.* packages and Flex' top-level package.

Swapping out ParsleyRule and using ParsleyDslRule instead activates this non-inheritance approach; under-the-hood it ensures that each test will run in isolation and that any invoked Parsley-aware DSL-function will use the same singletons/managed-objects for that test e.g. the same [MessageDispatcher] or EventDispatcher.

Some test developers use inheritance to implement a suite of similar tests - given Flex only supports single-inheritance - it is nice to have the inheritance-chain freed-up to allow them to continue to do so.

That's all there really is to it!

The change to Mockito-Flex that allows us to specify the application domain is included below, along with the two additional classes that are needed to make the mock-injection work:

--- mockito-flex/mockito/src/main/flex/org/mockito/integrations/flexunit4/MockitoRule.as (revision 87:8a8720f988e55bc8daf60144d8d73e63a8cc69b7)
+++ mockito-flex/mockito/src/main/flex/org/mockito/integrations/flexunit4/MockitoRule.as (revision 87+:8a8720f988e5+)
@@ -10,6 +10,7 @@
 import org.flexunit.runners.model.FrameworkMethod;
 import org.flexunit.token.AsyncTestToken;
 import org.mockito.Mockito;
+import org.mockito.impl.AsmockADMockeryProvider;
 import org.mockito.integrations.currentMockito;
 
 public class MockitoRule extends MethodRuleBase implements IMethodRule
@@ -19,7 +20,7 @@
         super();
     }
 
-    private function classFor(test:Object):Class
+    protected function classFor(test:Object):Class
     {
         return Class(getDefinitionByName(getQualifiedClassName(test)));
     }
@@ -27,7 +28,7 @@
     override public function apply(base:IAsyncStatement, method:FrameworkMethod, test:Object):IAsyncStatement
     {
         var sequencer:StatementSequencer = new StatementSequencer();
-        currentMockito = new Mockito();
+        currentMockito = new Mockito(AsmockADMockeryProvider);
         sequencer.addStep(withMocksPreparation(classFor(test)));
         sequencer.addStep(withMocksAssignment(classFor(test), test));
         sequencer.addStep(base);

package org.mockito.impl
{
    import org.mockito.api.MockCreator;
    import org.mockito.api.MockInterceptor;
    import org.mockito.api.MockeryProvider;
    import org.mockito.api.SequenceNumberGenerator;
    import org.mockito.api.SequenceNumberTracker;

    /**
     *
     * Implementation of the mockery provider that creates asmock based implementation
     */
    public class AsmockADMockeryProvider implements MockeryProvider
    {
        private var mockInterceptor:MockInterceptor;

        private var mockCreator:AsmockADMockery;

        /**
         * Creates mockery provider
         */
        public function AsmockADMockeryProvider(sequenceNumberGenerator:SequenceNumberGenerator, sequenceNumberTracker:SequenceNumberTracker)
        {
            mockInterceptor = new MockInterceptorImpl(sequenceNumberTracker);
            mockCreator = new AsmockADMockery(mockInterceptor, sequenceNumberGenerator);
        }

        /**
         * Returns MockCreator
         * @return implementation of MockCreator
         */
        public function getMockCreator():MockCreator
        {
            return mockCreator;
        }

        /**
         * Returns MockInterceptor
         * @return implementation of MockInterceptor
         *
         */
        public function getMockInterceptor():MockInterceptor
        {
            return mockInterceptor;
        }
    }
}

package org.mockito.impl
{
    import asmock.framework.MockRepository;
    import asmock.framework.asmock_internal;

    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.IEventDispatcher;
    import flash.system.ApplicationDomain;
    import flash.utils.Dictionary;

    import org.flemit.reflection.MethodInfo;
    import org.flemit.reflection.Type;
    import org.floxy.IInvocation;
    import org.mockito.api.Invocation;
    import org.mockito.api.MockCreator;
    import org.mockito.api.MockInterceptor;
    import org.mockito.api.SequenceNumberGenerator;

    use namespace asmock_internal;

    /**
     * Asmock bridge. Utilizes asmock facilities to create mock objects.
     * @private
     */
    public class AsmockADMockery extends MockRepository implements MockCreator
    {
        public var interceptor:MockInterceptor;

        protected var _names:Dictionary = new Dictionary();

        private var sequenceNumberGenerator:SequenceNumberGenerator;

        public function AsmockADMockery(interceptor:MockInterceptor, sequenceNumberGenerator:SequenceNumberGenerator)
        {
            super(new ADProxyRepository(ApplicationDomain.currentDomain));
            this.interceptor = interceptor;
            this.sequenceNumberGenerator = sequenceNumberGenerator;
        }

        /**
         * A factory method that creates Invocation out of asmock invocation
         * @param invocation asmock invocation object
         * @return mockito invocation
         *
         */
        public function createFrom(invocation:IInvocation):Invocation
        {
            var niceMethodName:String = new AsmockMethodNameFormatter().extractFunctionName(invocation.method.fullName);
            return new InvocationImpl(invocation.invocationTarget,
                    invocation.method.fullName,
                    invocation.arguments,
                    new AsmockOriginalCallSeam(invocation),
                    sequenceNumberGenerator.next());
        }

        public function prepareClasses(classes:Array, calledWhenClassesReady:Function, calledWhenPreparingClassesFailed:Function = null):void
        {
            var dispatcher:IEventDispatcher = super.prepare(classes);
            var repositoryPreparedHandler:Function = function (e:Event):void
            {
                calledWhenClassesReady();
            };
            
            var repositoryPreparationFailed:Function = function (e:Event):void
            {
                if (calledWhenPreparingClassesFailed != null)
                    calledWhenPreparingClassesFailed();
            };
            dispatcher.addEventListener(Event.COMPLETE, repositoryPreparedHandler);
            dispatcher.addEventListener(ErrorEvent.ERROR, repositoryPreparationFailed);
        }

        public function mock(clazz:Class, name:String = null, constructorArgs:Array = null):*
        {
            if (name == null)
            {
                name = Type.getType(clazz).name;
            }
            var mock:Object = createStrict(clazz, constructorArgs);
            registerAlias(mock, name);
            return mock;
        }

        override asmock_internal function methodCall(invocation:IInvocation, target:Object, method:MethodInfo, arguments:Array):*
        {
            return interceptor.methodCalled(createFrom(invocation));
        }

        /**
         *
         * @param mock
         * @param name
         */
        public function registerAlias(mock:Object, name:String):void
        {
            _names[mock] = name;
        }
    }
}

Epilogue


Although I doubt anyone has been waiting, apologies for taking so long to wrap this all up.
All the same, it was an interesting exercise to figure all this out.

Ironically, I likely will not be engaging in any Flex development going forward, as I have recently turned my attention to iOS and SCALA.
However I will commit to getting the Mockito-Flex changes pushed to my fork on BitBucket and raise a pull request with Loomis, the author. DONE!

Thanks for reading and comments are welcome as always.

Thursday, June 16, 2011

Darren on Flex: FlexUnit4 Testing with Parsley - Testing with a Parsley-Aware DSL

In Part-4 I showed how to use [RunWith] and [Rule] to locate and hide Parsley context initialization; with the reduced duplication in this approach tests are left to focus on what is important - testing.

In Part-5, I'll show how to reduce the noise hide the complexities of Parsley Messaging, used to implement decoupling between components, by extending the Fluint-aware DSL introduced in Part-3 to be Parsley-aware.

The 'test' code re-re-re-revisted...

Hiding Messages
As mentioned, this improved DSL attempts to hide uses of the Parsley Framework. The approach is limited to hiding usage of the Parsley Messaging API, but supports a comfortable balance of declarative and imperative coding seen in story-style testing i.e. given-when-then.

We retain the declarative approach to dependency injection i.e. with [Inject], yet this approach is less fitting for messaging/coupling management in a test(-method). Story tests tend to read as a sequence of imperative commands to prepare (Given), actuate (When) and inspect (Then) the object-under-test. In this style you often specify which message to dispatch and which to listen out for... and when... and for how long (timeout). You have the opportunity to state your messaging intentions clearly, in-line, that is, not having a test distributed over several methods (some of which being annotated with [MessageHandler(...)] metadata tags).

package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.GetPersonCommandComponentTestContext;
    import com.darrenbishop.support.flexunit.parsley.ParsleyHelper;
    
    import org.flexunit.asserts.fail;
    import flash.events.ErrorEvent;
    import org.flexunit.assertThat;
    import org.hamcrest.object.equalTo;
    import com.darrenbishop.support.create;
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.flexunit.parsley.ParsleyRule;
    
    public class GetPersonCommandParsleyRuleDSLComponentTest extends ParsleyHelper {
        [Rule]
        public var rule:ParsleyRule = new ParsleyRule(GetPersonCommandComponentTestContext);
        
        [Inject]
        public var service:StubDirectoryService;
        
        [Inject]
        public var command:GetPersonCommand;
        
        public var dodgyPerson:Person;
        
        public var soundPerson:Person;
        
        [Before]
        public function primeService():void {
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            
            service.add(dodgyPerson, soundPerson);
            
            service.dodgyPerson = dodgyPerson.id;
            service.soundPerson = soundPerson.id;
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            dispatchMessage(PersonEvent.newLookup(soundPerson.id));
            waitForMessage(PersonEvent.FOUND, 1000);
            thenAssert(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
        }
        
        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            dispatchMessage(PersonEvent.newLookup(dodgyPerson.id));
            waitForMessage(ErrorEvent.ERROR, 1000);
            thenAssert(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
        }
    }
}
Note the test class now extends ParsleyHelper, building on the inheritance approach to provide access to the messaging-DSL methods; you'll see shortly that this new helper class extends from AsyncHelper, so the async-DSL methods are still available.

There are a number of changes to note and to make them clear I've provided a diff generated from the test above and that presented in Part-4:

--- C:/Dev/workspaces/flex/parsley-flexunit/src/test/flex/com/darrenbishop/crm/directory/application/GetPersonCommandParsleyRuleComponentTest.as Wed Jun 15 21:00:39 2011
+++ C:/Dev/workspaces/flex/parsley-flexunit/src/test/flex/com/darrenbishop/crm/directory/application/GetPersonCommandParsleyRuleDSLComponentTest.as Wed Jun 15 20:54:17 2011
@@ -4,3 +4,3 @@
     import com.darrenbishop.support.create;
-    import com.darrenbishop.support.flexunit.async.AsyncHelper;
+    import com.darrenbishop.support.flexunit.parsley.ParsleyHelper;
     import com.darrenbishop.support.flexunit.parsley.ParsleyRule;
@@ -8,3 +8,2 @@
     import flash.events.ErrorEvent;
-    import flash.events.EventDispatcher;
     
@@ -12,5 +11,4 @@
     import org.hamcrest.object.equalTo;
-    import org.spicefactory.parsley.core.messaging.MessageProcessor;
     
-    public class GetPersonCommandParsleyRuleComponentTest extends AsyncHelper {
+    public class GetPersonCommandParsleyRuleDSLComponentTest extends ParsleyHelper {
         [Rule]
@@ -18,5 +16,2 @@
         
-        [MessageDispatcher]
-        public var dispatcher:Function;
-        
         [Inject]
@@ -31,8 +26,4 @@
         
-        private var eventDispatcher:EventDispatcher;
-        
         [Before]
         public function primeService():void {
-            eventDispatcher = new EventDispatcher();
-            
             dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
@@ -46,16 +37,6 @@
         
-        [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
-        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
-            eventDispatcher.dispatchEvent(toEvent(error));
-        }
-        
-        [MessageHandler(selector='PersonEvent.found')]
-        public function passItOn(event:PersonEvent):void {
-            eventDispatcher.dispatchEvent(event);
-        }
-        
         [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
         public function resultDoesNotManglePersonFound():void {
-            dispatcher(PersonEvent.newLookup(soundPerson.id));
-            waitFor(eventDispatcher, PersonEvent.FOUND, 1000);
+            dispatchMessage(PersonEvent.newLookup(soundPerson.id));
+            waitForMessage(PersonEvent.FOUND, 1000);
             thenAssert(function(event:PersonEvent, data:*):void {
@@ -68,4 +49,4 @@
         public function resultVerifiesPersonFound():void {
-            dispatcher(PersonEvent.newLookup(dodgyPerson.id));
-            waitFor(eventDispatcher, ErrorEvent.ERROR, 1000);
+            dispatchMessage(PersonEvent.newLookup(dodgyPerson.id));
+            waitForMessage(ErrorEvent.ERROR, 1000);
             thenAssert(function(event:ErrorEvent, data:*):void {
Counting the new lines (6) and the dropped lines (20, not including blank-lines) reveals a net 14 line reduction of boilerplate code that would otherwise be repeated as your tests grow in quantity and size.

With this improved DSL, tests become even simpler to read.

[MessageDispatcher] - refactored
As mentioned all the explicit Parsley Messaging Framework usage has been pulled up to the ParsleyHelper super class:
package com.darrenbishop.support.flexunit.parsley {
    import com.darrenbishop.support.flexunit.async.AsyncHelper;
    
    import flash.events.Event;
    import flash.events.EventDispatcher;
    
    import org.spicefactory.parsley.core.messaging.MessageProcessor;

    public class ParsleyHelper extends AsyncHelper {
        [MessageDispatcher]
        public var dispatcher:Function;
        
        protected var eventDispatcher:EventDispatcher;
        
        [Before]
        public function prepareEventDispatcher():void {
            eventDispatcher = new EventDispatcher();
        }
        
        [MessageError]
        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
            eventDispatcher.dispatchEvent(toEvent(error));
        }
        
        [MessageHandler]
        public function passItOn(msg:Event):Boolean {
            return eventDispatcher.dispatchEvent(msg);
        }
        
        protected function dispatchEvent(event:Event):void {
            eventDispatcher.dispatchEvent(event);
        }
        
        protected function dispatchMessage(event:Event):void {
            dispatcher(event);
        }
        
        protected function dispatchError(error:Error):void {
            eventDispatcher.dispatchEvent(toEvent(error));
        }
        
        protected function waitForMessage(eventType:String, timeout:Number=1000):void {
            waitFor(eventDispatcher, eventType, timeout);
        }
        
        protected function waitForError(eventType:String, timeout:Number=1000):void {
            waitForMessage(eventType, timeout);
        }
    }
}
Here we have all the methods pertinent to Parsley messaging, laid out and self explanatory.

Furthermore this approach does a good job of hiding usage of the Flex Event mechanism; this messaging-DSL removes the need to interact explicitly with an EventDispatcher.

What's Next...

Part 6: All That, But With Mocking
So I've got one more Ace up my sleeve - mock-injection. At this point I'm not even sure this is a good idea or whether there's a place for this technique in any practical testing scenario. I have recently read the Spring documentation on this and have since seen this done in enough places to be convinced there is value in it for component/sub-system testing.

Nonetheless, I figured it out and implemented it; the implementation in fact requires some modifications, including some to Mockito-Flex classes. I've contributed these code changes to the Mockito-Flex project, which I believe will be rolled in or otherwise accommodated soon; you can read more about it here

Wednesday, April 20, 2011

Darren on Flex: FlexUnit4 Testing with Parsley - Improved Parsley Support with FlexUnit's [RunWith(...)] & [Rule]

In Part-3 I showed how a DSL can be used to reduce the complexity of asynchronous testing when using the Fluint Sequence API. With this DSL I was also able to remove several lines of boilerplate code. These might be modest improvements, but the introduction of a DSL paves the way for more concise tests.

In Part-4, I'll show how to integrate Parsley into FlexUnit by using the [RunWith] and [Rule] hook mechanisms to make FlexUnit do the heavy lifting i.e. context initialization and dependency injection.

The 'test' code re-re-revisted...

As mentioned there are two approaches, [RunWith] and [Rule]. I had previously held-back from exploring the [Rule] option as it comes with FlexUnit 4.1, which up until very recently, had only been available in beta. I explore this second option later in this post, but first...
The [RunWith] approach
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.GetPersonCommandComponentTestContext;GetPersonCommandComponentTestContext;
    import com.darrenbishop.support.flexunit.parsley.ParsleyRunner;ParsleyRunner;
    
    import org.flexunit.asserts.fail;
    import flash.events.ErrorEvent;
    import org.flexunit.assertThat;
    import org.hamcrest.object.equalTo;
    import com.darrenbishop.support.create;
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.flexunit.async.AsyncHelper;
    import org.spicefactory.parsley.core.context.Context;
    import flash.events.EventDispatcher;
    import org.spicefactory.parsley.core.messaging.MessageProcessor;
    
    [RunWith('com.darrenbishop.support.flexunit.parsley.ParsleyRunner')]
    [Context('com.darrenbishop.crm.directory.GetPersonCommandComponentTestContext')]
    public class GetPersonCommandParsleyRunnerComponentTest extends AsyncHelper {
        [MessageDispatcher]
        public var dispatcher:Function;
        
        [Inject]
        public var context:Context;
        
        [Inject]
        public var service:StubDirectoryService;
        
        [Inject]
        public var command:GetPersonCommand;
        
        public var dodgyPerson:Person;
        
        public var soundPerson:Person;
        
        private var eventDispatcher:EventDispatcher;
        
        [Before]
        public function primeService():void {
            eventDispatcher = new EventDispatcher();
            
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            
            service.add(dodgyPerson, soundPerson);
            
            service.dodgyPerson = dodgyPerson.id;
            service.soundPerson = soundPerson.id;
        }
        
        [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
            eventDispatcher.dispatchEvent(toEvent(error));
        }
        
        [MessageHandler(selector='PersonEvent.found')]
        public function passItOn(event:PersonEvent):void {
            eventDispatcher.dispatchEvent(event);
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            dispatcher(PersonEvent.newLookup(soundPerson.id));
            waitFor(eventDispatcher, PersonEvent.FOUND, 1000);
            thenAssert(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
        }
        
        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            dispatcher(PersonEvent.newLookup(dodgyPerson.id));
            waitFor(eventDispatcher, ErrorEvent.ERROR, 1000);
            thenAssert(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
        }
    }
}
I declare the ParsleyRunner class as the default attribute to the [RunWith] metadata tag; I import and reference the same (at the top of the excerpt) to ensure the runner is linked in. This highlights one of the fundamental problems with this approach: you must repeat you intention too many times - once should be enough.

The same goes for the GetPersonCommandComponentTestContext; the runner needs to be told somehow which Parsley context to initialize. Perhaps there are better ways to communicate to the runner which context to use, with static members maybe, but the problem persists for the runner itself so there's little value in exploring such an enhancement.

The only other significant change is the [Before] annotated method; this method is no-longer responsible for building the Parsley context so I renamed it from initializeContext() to primeService(), as that's all it now does; it primes the StubDirectoryService with people and the id to trigger the error case.

Introducing the the ParsleyRunner...
package com.darrenbishop.support.flexunit.parsley {
    import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;
    
    import flex.lang.reflect.metadata.MetaDataAnnotation;
    
    import org.flexunit.internals.runners.statements.IAsyncStatement;
    import org.flexunit.internals.runners.statements.StatementSequencer;
    import org.flexunit.runner.notification.IRunNotifier;
    import org.flexunit.runners.BlockFlexUnit4ClassRunner;
    import org.flexunit.runners.model.FrameworkMethod;

    public class ParsleyRunner extends BlockFlexUnit4ClassRunner {
        public static const CONTEXT_METADATA_NAME:String = 'Context';
        
        private var contextDescriptor:Class;
        
        public function ParsleyRunner(cls:Class) {
            super(cls);
            var ctxMetaData:MetaDataAnnotation = testClass.klassInfo.getMetaData(CONTEXT_METADATA_NAME);
            this.contextDescriptor = classFor(ctxMetaData.defaultArgument.key)
        }
        
        private function classFor(name:String):Class {
            return Class(getDefinitionByName(getQualifiedClassName(name)));
        }
        
        private function withParsleyContextBuilt(test:Object):IAsyncStatement {
            return new BuildParsleyContext(test, contextDescriptor);
        }

        protected override function withBefores(method:FrameworkMethod, test:Object, statement:IAsyncStatement):IAsyncStatement {
            if (contextDescriptor) {
                var sequencer:StatementSequencer = new StatementSequencer();
                sequencer.addStep(withParsleyContextBuilt(test));
                sequencer.addStep(super.withBefores(method, test, statement));
                return sequencer;
            }
            else {
                return super.withBefores(method, test, statement);
            }
        }
    }
}
I determine the class that describes the Parsley context for this test in the constructor, by retrieving the default attribute value of the [Context] metadata tag; this I consider to be a one-off class-level operation. The withBefores(...) template method is invoked before each test method, to contribute to the preparation of the environment in which the test method will execute; this method delegates to withParsleyContextBuilt(...) to generate a BuildParsleyContext command-object. BuildParsleyContext is not a Parsley command, as in the one being tested - it's just the pattern/convention used in the FlexUnit internals.
BuildParsleyContext, the heavy lifter
This command-object does all the work using the Parsley ActionScript3 API for context initialization:
package com.darrenbishop.support.flexunit.parsley {
    import flash.utils.getDefinitionByName;
    
    import org.flexunit.internals.runners.statements.AsyncStatementBase;
    import org.flexunit.internals.runners.statements.IAsyncStatement;
    import org.flexunit.token.AsyncTestToken;
    import org.spicefactory.parsley.core.context.Context;
    import org.spicefactory.parsley.flex.FlexContextBuilder;

    public class BuildParsleyContext extends AsyncStatementBase implements IAsyncStatement {
        protected var test:Object;
        protected var contextDescriptor:Class;
        
        public function BuildParsleyContext(target:Object, contextDescriptor:Class) {
            this.test = target;
            this.contextDescriptor = contextDescriptor;
        }
        
        public function evaluate(parentToken:AsyncTestToken):void {
            buildContext(parentToken);
        }

        private function buildContext(parentToken:AsyncTestToken):void {
            var error:Error = null;
            try {
                var ctx:Context = FlexContextBuilder.build(contextDescriptor);
                ctx.createDynamicContext().addObject(test);
            }
            catch (e:Error) {
                error = e;
            }
            
            parentToken.sendResult(error);
        }
    }
}
Basically, what once lived and would have been replicated in the [Before] methods of each and every Parsley-powered test, now lives here; but again, there's nothing new, just a relocation of code to somewhere more appropriate for re-use.

And that's it for the [RunWith] approach, but now with the relase of FlexUnit-4.1, we have...

The [Rule] approach
This hook mechanism completely solves the problem of having to declare the runner implementation class as a string in a metadata attribute:
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.GetPersonCommandComponentTestContext;
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.create;
    import com.darrenbishop.support.flexunit.async.AsyncHelper;
    import com.darrenbishop.support.flexunit.parsley.ParsleyRule;
    
    import flash.events.ErrorEvent;
    import flash.events.EventDispatcher;
    
    import org.flexunit.assertThat;
    import org.hamcrest.object.equalTo;
    import org.spicefactory.parsley.core.messaging.MessageProcessor;
    
    public class GetPersonCommandParsleyRuleComponentTest extends AsyncHelper {
        [Rule]
        public var rule:ParsleyRule = new ParsleyRule(GetPersonCommandComponentTestContext);
        
        [MessageDispatcher]
        public var dispatcher:Function;
        
        [Inject]
        public var service:StubDirectoryService;
        
        [Inject]
        public var command:GetPersonCommand;
        
        public var dodgyPerson:Person;
        
        public var soundPerson:Person;
        
        private var eventDispatcher:EventDispatcher;
        
        [Before]
        public function primeService():void {
            eventDispatcher = new EventDispatcher();
            
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            
            service.add(dodgyPerson, soundPerson);
            
            service.dodgyPerson = dodgyPerson.id;
            service.soundPerson = soundPerson.id;
        }
        
        [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
            eventDispatcher.dispatchEvent(toEvent(error));
        }
        
        [MessageHandler(selector='PersonEvent.found')]
        public function passItOn(event:PersonEvent):void {
            eventDispatcher.dispatchEvent(event);
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            dispatcher(PersonEvent.newLookup(soundPerson.id));
            waitFor(eventDispatcher, PersonEvent.FOUND, 1000);
            thenAssert(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
        }
        
        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            dispatcher(PersonEvent.newLookup(dodgyPerson.id));
            waitFor(eventDispatcher, ErrorEvent.ERROR, 1000);
            thenAssert(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
        }
    }
}
The shift can be simply thought of as declaring the runner implementation as a member variable i.e. rule and annotating it with the [Rule] metadata tag, rather than as a string metadata attribute. Of course, now it's no longer a runner, but a rule and extends a different parent class and implements slightly different template methods... but for the most part it's the same.
The ParsleyRule to rule it all
As mentioned there are few differences, but one welcome difference, now that the rule is declared and initialized as an instance variable, is that in doing so the constructor is called and any arbitrary arguments can be passed in. I use this to communicate which Parsley context to initialize, by passing in the class that defines it.
package com.darrenbishop.support.flexunit.parsley {
    import org.flexunit.internals.runners.statements.IAsyncStatement;
    import org.flexunit.internals.runners.statements.MethodRuleBase;
    import org.flexunit.internals.runners.statements.StatementSequencer;
    import org.flexunit.rules.IMethodRule;
    import org.flexunit.runners.model.FrameworkMethod;
    import org.flexunit.token.AsyncTestToken;

    public class ParsleyRule extends MethodRuleBase implements IMethodRule {
        private var contextDescriptor:Class;
        
        public function ParsleyRule(contextDescriptor:Class) {
            super();
            this.contextDescriptor = contextDescriptor;
        }
        
        override public function apply(base:IAsyncStatement, method:FrameworkMethod, test:Object):IAsyncStatement {
            var sequencer:StatementSequencer = new StatementSequencer();
            sequencer.addStep(withParsleyContextBuilt(test));
            sequencer.addStep(base);
            return super.apply(sequencer, method, test);
        }
        
        private function withParsleyContextBuilt(test:Object):IAsyncStatement {
            return new BuildParsleyContext(test, contextDescriptor);
        }
        
        override public function evaluate(parentToken:AsyncTestToken):void {
            super.evaluate(parentToken);
            proceedToNextStatement();
        }
    }
}
The [Rule] approach has the benefit that it does not exhaust the use of inheritance, nor does it use [RunWith], both of which are use-once mechanisms.

There is now the benefit that multiple rules can be defined and used in conjunction with each other; this removes the impediments to mocking described in Part-1

I have actually succeeded in mocking a DirectoryService instance and embedding that instance into the Parsley context ready for dependency-injection, but that's another blog :-)

What's Next...

Part 5: Testing with a Parsley-Aware DSL
The final (planned) part of this blog series will demonstrate the ParsleyHelper base class. ParsleyHelper extends from AsyncHelper, introduced in Part-3, to add a layer of abstraction for all the Parsley bits and pieces; this improved DSL implementation allows for the removal of all reference to anything-Parsley

... except for the ParsleyRule

... and the ParsleyHelper.

Tuesday, April 19, 2011

Darren on Flex: FlexUnit4 Testing with Parsley - Hiding Fluint Sequences with a Flow-based DSL

In Part-2 I showed how to implement a component-test, with some of the setup delegated to Parsley to leverage IoC and DI.

In Part-3, I'll show how to hide the Fluint Sequence API, used for asynchronous testing, behind an embedded asynchronous-DSL. The DSL design presented only really serves to seed an idea; anyone can implement a DSL, using different verb-names for methods and statement construction.

The 'test' code re-revisted...

package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.GetPersonCommandComponentTestContext;
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.create;
    import com.darrenbishop.support.flexunit.async.AsyncHelper;
    
    import flash.events.ErrorEvent;
    import flash.events.EventDispatcher;
    
    import org.flexunit.assertThat;
    import org.hamcrest.object.equalTo;
    import org.spicefactory.parsley.core.context.Context;
    import org.spicefactory.parsley.core.messaging.MessageProcessor;
    import org.spicefactory.parsley.flex.FlexContextBuilder;
    
    public class GetPersonCommandDSLComponentTest extends AsyncHelper {
        [MessageDispatcher]
        public var dispatcher:Function;
        
        [Inject]
        public var context:Context;
        
        [Inject]
        public var service:StubDirectoryService;
        
        [Inject]
        public var command:GetPersonCommand;
        
        public var dodgyPerson:Person;
        
        public var soundPerson:Person;
        
        private var eventDispatcher:EventDispatcher;
        
        [Before]
        public function initializeContext():void {
            var ctx:Context = FlexContextBuilder.build(GetPersonCommandComponentTestContext);
            ctx.createDynamicContext().addObject(this);
            
            eventDispatcher = new EventDispatcher();
            
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            
            service.add(dodgyPerson, soundPerson);
            
            service.dodgyPerson = dodgyPerson.id;
            service.soundPerson = soundPerson.id;
        }
        
        [After]
        public function destroyContext():void {
            context.destroy();
            eventDispatcher = null;
        }
        
        [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
            eventDispatcher.dispatchEvent(toEvent(error));
        }
        
        [MessageHandler(selector='PersonEvent.found')]
        public function passItOn(event:PersonEvent):void {
            eventDispatcher.dispatchEvent(event);
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            dispatcher(PersonEvent.newLookup(soundPerson.id));
            waitFor(eventDispatcher, PersonEvent.FOUND, 1000);
            thenAssert(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
        }
        
        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            dispatcher(PersonEvent.newLookup(dodgyPerson.id));
            waitFor(eventDispatcher, ErrorEvent.ERROR, 1000);
            thenAssert(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
        }
    }
}
Note that the test class now extends AsyncHelper, that is, I use inheritance to provide access to the async-DSL methods. An alternative approach is that adopted by Mockito-Flex, where a global object is used as/to share state between a suite of global functions implementing the mocking-DSL. I believe this is a safe approach as the Flex execution model is 'single-threaded'... I might explore this approach and free-up inheritance for local uses.

Anyway, my main objective in doing this is to remove any explicit use of the Fluint Sequence API and provide an abstraction that is a bit easier to use and read. To make the refactorings and changes a little more clear, I've provided a diff below generated against the test above and that from Part-2

--- C:/Dev/workspaces/flex/parsley-flexunit/src/test/flex/com/darrenbishop/crm/directory/application/GetPersonCommandComponentTest.as Fri Jun 17 07:26:56 2011
+++ C:/Dev/workspaces/flex/parsley-flexunit/src/test/flex/com/darrenbishop/crm/directory/application/GetPersonCommandDSLComponentTest.as Fri Jun 17 07:23:37 2011
@@ -4,2 +4,3 @@
     import com.darrenbishop.support.create;
+    import com.darrenbishop.support.flexunit.async.AsyncHelper;
     
@@ -9,4 +10,2 @@
     import org.flexunit.assertThat;
-    import org.fluint.sequence.SequenceRunner;
-    import org.fluint.sequence.SequenceWaiter;
     import org.hamcrest.object.equalTo;
@@ -16,3 +15,3 @@
     
-    public class GetPersonCommandComponentTest {
+    public class GetPersonCommandDSLComponentTest extends AsyncHelper {
         [MessageDispatcher]
@@ -35,5 +34,3 @@
         
-        private var sequence:SequenceRunner;
-        
-        [Before(async)]
+        [Before]
         public function initializeContext():void {
@@ -44,4 +41,2 @@
             
-            sequence = new SequenceRunner(this);
-            
             dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
@@ -59,8 +54,7 @@
             eventDispatcher = null;
-            sequence = null;
         }
-
+        
         [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
         public function rethrowIt(processor:MessageProcessor, error:Error):void { 
-            eventDispatcher.dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, error.message));
+            eventDispatcher.dispatchEvent(toEvent(error));
         }
@@ -75,6 +69,4 @@
             dispatcher(PersonEvent.newLookup(soundPerson.id));
-            
-            sequence.addStep(new SequenceWaiter(eventDispatcher, PersonEvent.FOUND, 1000));
-            
-            sequence.addAssertHandler(function(event:PersonEvent, data:*):void {
+            waitFor(eventDispatcher, PersonEvent.FOUND, 1000);
+            thenAssert(function(event:PersonEvent, data:*):void {
                 assertThat(event.type, equalTo('PersonEvent.found'));
@@ -82,4 +74,2 @@
             });
-            
-            sequence.run();
         }
@@ -89,10 +79,6 @@
             dispatcher(PersonEvent.newLookup(dodgyPerson.id));
-            
-            sequence.addStep(new SequenceWaiter(eventDispatcher, ErrorEvent.ERROR, 1000));
-            
-            sequence.addAssertHandler(function(event:ErrorEvent, data:*):void {
+            waitFor(eventDispatcher, ErrorEvent.ERROR, 1000);
+            thenAssert(function(event:ErrorEvent, data:*):void {
                 assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
             });
-            
-            sequence.run();
         }
By counting the added (8) and subtracted (14) lines reveals a net reduction by 6 lines - a modest improvement. I could have indulged a bit more and come up with a nice Given, When, Then conforming DSL, but too much thought would have had to go into getting the semantics right, so I defer that for another day.

The important thing is I have achieved my goal, stated above.

async - refactored
So as discussed, all the Fluint Sequence usage has been pulled up into the AsyncHelper base class:
package com.darrenbishop.support.flexunit {
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    
    import mx.events.FlexEvent;
    
    import org.fluint.sequence.*;

    public class AsyncHelper {
        protected var sequence:SequenceRunner;
        
        [Before(async)]
        public function setUpSequence():void {
            sequence = new SequenceRunner(this);
        }
        
        [After(async)]
        public function tearDownSequence():void {
            sequence = null;
        }
        
        // Primitive steps
        
        protected function waitFor(dispatcher:EventDispatcher, eventType:String=FlexEvent.VALUE_COMMIT, timeout:Number=100):void {
            sequence.addStep(new SequenceWaiter(dispatcher, eventType, timeout));
        }
        
        protected function dispatch(dispatcher:EventDispatcher, event:Event):void {
            sequence.addStep(new SequenceEventDispatcher(dispatcher, event));
        }
        
        protected function assert(handler:Function, passThroughData:*):void {
            sequence.addAssertHandler(handler, passThroughData);
        }
        
        protected function run():void {
            sequence.run();
        }
        
        protected function toEvent(error:Error):ErrorEvent {
            return new ErrorEvent(ErrorEvent.ERROR, false, false, error.message);
        }
        
        // Composite steps
        
        protected function thenAssert(handler:Function, passThroughData:*=null):void {
            assert(handler, passThroughData || {});
            run();
        }
    }
}
Notice that this test base-class defines setup and teardown behaviour, using [Before] and [After] metadata tags. FlexUnit supports [Before] and [After] inheritance, using an accumulator strategy rather than the standard-OO override strategy:
  • [Before] annotated methods are applied from the root of the inheritance chain down, prior to a test method invocation
  • [After] annotated methods are applied from bottom to top
Presumably, this provides symmetry to the setup and teardown of state and fixtures, with each level of inheritance potentially relying on the effects of the levels above.

The AsyncHelper class is pretty self explanatory: the [Before] and [After] methods are nothing new - they just manage the existence of the sequence object. The rest of the methods just manipulate the sequence object, but again, in no new ways; they are given verb or verb-phrases for names, thus self-describing what they do.

The test author will need to know about these inherited methods to use them - not really a problem these days... we all hit Ctrl+Space, right?

The real value in DSLs comes when developers (or even non-developers, if you happen to pair with a QA guy or a BA) who are not the original author of the tests (or whatever) must read them; they will be able to discern the embedded flow/logic/intention/expectation a lot easier.

What's Next...

Part 4: Improved Parsley Support with FlexUnit's [RunWith(...)][Rule]
I'll introduce the ParsleyRunner, which facilitates integration of Parsley into FlexUnit testing, using the [RunWith] metadata tag. Also, with the recent release of FlexUnit 4.1, I'll implement improved Parsley-FlexUnit integration using the [Rule] metadata tag.

Sunday, April 17, 2011

Darren on Flex: FlexUnit4 Testing with Parsley - Asynchronous Testing with Parsley and Fluint Sequences

In Part-1 I showed a pure-unit test, completely managed outside Parsley.

In Part-2 I'll show how to implement a component-test, with some of the setup delegated to Parsley to leverage IoC and DI.

The 'test' code: revisited...

Again I'll jump straight into the test class itself
The FlexUnit4 GetPersonCommandComponentTest
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.GetPersonCommandComponentTestContext;
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.create;
    
    import flash.events.ErrorEvent;
    import flash.events.EventDispatcher;
    
    import org.flexunit.assertThat;
    import org.fluint.sequence.SequenceRunner;
    import org.fluint.sequence.SequenceWaiter;
    import org.hamcrest.object.equalTo;
    import org.spicefactory.parsley.core.context.Context;
    import org.spicefactory.parsley.core.messaging.MessageProcessor;
    import org.spicefactory.parsley.flex.FlexContextBuilder;
    
    public class GetPersonCommandComponentTest {
        [MessageDispatcher]
        public var dispatcher:Function;
        
        [Inject]
        public var context:Context;
        
        [Inject]
        public var service:StubDirectoryService;
        
        [Inject]
        public var command:GetPersonCommand;
        
        public var dodgyPerson:Person;
        
        public var soundPerson:Person;
        
        private var eventDispatcher:EventDispatcher;
        
        private var sequence:SequenceRunner;
        
        [Before(async)]
        public function initializeContext():void {
            var ctx:Context = FlexContextBuilder.build(GetPersonCommandComponentTestContext);
            ctx.createDynamicContext().addObject(this);
            
            eventDispatcher = new EventDispatcher();
            
            sequence = new SequenceRunner(this);
            
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            
            service.add(dodgyPerson, soundPerson);
            
            service.dodgyPerson = dodgyPerson.id;
            service.soundPerson = soundPerson.id;
        }
        
        [After]
        public function destroyContext():void {
            context.destroy();
            eventDispatcher = null;
            sequence = null;
        }

        [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
            eventDispatcher.dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, error.message));
        }
        
        [MessageHandler(selector='PersonEvent.found')]
        public function passItOn(event:PersonEvent):void {
            eventDispatcher.dispatchEvent(event);
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            dispatcher(PersonEvent.newLookup(soundPerson.id));
            
            sequence.addStep(new SequenceWaiter(eventDispatcher, PersonEvent.FOUND, 1000));
            
            sequence.addAssertHandler(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
            
            sequence.run();
        }
        
        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            dispatcher(PersonEvent.newLookup(dodgyPerson.id));
            
            sequence.addStep(new SequenceWaiter(eventDispatcher, ErrorEvent.ERROR, 1000));
            
            sequence.addAssertHandler(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
            
            sequence.run();
        }
    }
}
There's one quick thing to point out here - this component-test is actually simpler than the unit-test discussed in Part-1, with one fewer tests as there's no value in verifying the service-call-sequencing again. Incidentally, the remaining two tests still provide 75% coverage (for the same reasons as before).

Right, I'll breeze past dispatcher, command and service; these are standard injections you'd use while programming with Parsley.

A lesser known fact about context: no declaration is required for this dependency i.e. in GetPersonCommandTestContext; Parsley will detect the expectation of an object of type org.spicefactory.parsley.core.context.Context and will inject that instance in which this is initialized by, that is, the context doing the injections will be injected.
Pretty neat, eh. I will show how to leverage this later in the series.

The next two variables, dodgyPerson and soundPerson, reflect a kind-of agreement between the StubDirectoryService implementation that is injected and the test, which is something like: if you request dodgyPerson, I will send back soundPerson; I use this to implement the negative-test.
Again, mocking would be the better approach here, where interactions would then be dictated rather than just implied... moving on...

The eventDispatcher is used here to bridge the Parsley Messaging Framework and FlexUnit (Fluint) Asynchronous Testing.

The sequence object is the key to FlexUnit (Fluint) Asynchronous Testing; it must be used with the async attribute in [Before], [After] or [Test] metadata tags.

Where the Magic Happens... Well, Some of It Anyway
initializeContext() is where the Parsley context is initialized and this test instance along with it.
        [Before(async)]
        public function initializeContext():void {
            var ctx:Context = FlexContextBuilder.build(GetPersonCommandComponentTestContext);
            ctx.createDynamicContext().addObject(this);
            
            eventDispatcher = new EventDispatcher();
            
            sequence = new SequenceRunner(this);
            
            dodgyPerson = create(Person, {'id': 1, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6803225});
            soundPerson = create(Person, {'id': 2, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            
            service.add(dodgyPerson, soundPerson);
            
            service.dodgyPerson = dodgyPerson.id;
            service.soundPerson = soundPerson.id;
        }
From discussions I've had with my peers, it's the key insight and biggest stumbling block. Funnily enough, it's clearly documented here and here how to initialize a context programmatically, but I still needed help joinining the dots :-/

I borrowed those ideas and used them to implement a different approach:

  • One (potentially) monolithic FlexUnitApplicationContext can be broken down to contain only what's relevant to each test
  • The test method states which context to use, making it more clear/traceable what is in the test environment (think: Ctrl+click)
  • Different contexts allow for different service (interface) implementations i.e. less injection-by-id
  • Tools like FlashBuilder and Flexmojos are free to control (generate) the test application e.g. FlexUnitApplication

Both eventDispatcher and sequence are initialized - nothing exciting - and destroyContext() is also pretty straightforward.

Assignments to service.dodgyPerson, dodgyPerson service.soundPerson and soundPerson setup the agreement between the this test and the service.

Now let's review the rest of the code in method-pairs - this is is where the real magic happens...

Where the Real Magic Happens
First passItOn(...) and resultDoesNotManglePersonFound():
        [MessageHandler(selector='PersonEvent.found')]
        public function passItOn(event:PersonEvent):void {
            eventDispatcher.dispatchEvent(event);
        }
        
        [Test(async,description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            dispatcher(PersonEvent.newLookup(soundPerson.id));
            
            sequence.addStep(new SequenceWaiter(eventDispatcher, PersonEvent.FOUND, 1000));
            
            sequence.addAssertHandler(function(event:PersonEvent, data:*):void {
                assertThat(event.type, equalTo('PersonEvent.found'));
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            });
            
            sequence.run();
        }
Looking first at the test-method, you can quite easily read the sequence of events:
  1. I send off a person lookup request
  2. I wait for the person to be found
  3. I check the person is who I expect
Looking into the detail, the lookup request is sent using the Parsley injected dispatcher i.e. using the Parsley Messaging Framework, yet the test is using Fluint (FlexUnit) to wait for the found notification i.e. using the plain-vanilla Flex Event Mechanism; the two technologies must be bridged - this is where passItOn(...) comes in.

passItOn(...) is adorned with the [MessageHandler] metadata tag, which Parsley will detect during initialization and wire-up to its Messaging Framework. Now, the test instance will be informed when a person is found. When that happens, this method simply uses the eventDispatcher to pass the found event onto the Flex Event Mechanism; where this eventDispatcher is the same one used by Fluint, the test execution can now proceed.

Now for rethrowIt(...) and resultVerifiesPersonFound():

        [MessageError(type='com.darrenbishop.crm.directory.application.PersonEvent')]
        public function rethrowIt(processor:MessageProcessor, error:Error):void { 
            eventDispatcher.dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, error.message));
        }
        
        ...
        
        [Test(async,description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            dispatcher(PersonEvent.newLookup(dodgyPerson.id));
            
            sequence.addStep(new SequenceWaiter(eventDispatcher, ErrorEvent.ERROR, 1000));
            
            sequence.addAssertHandler(function(event:ErrorEvent, data:*):void {
                assertThat(event.text, equalTo(sprintf('Found person (%d) does not match lookup person (%d)', soundPerson.id, dodgyPerson.id)));
            });
            
            sequence.run();
        }
Again looking at the test-method first, you can see that:
  1. I send off a lookup request for the dodgy-person, thus triggering an error
  2. I wait for an error
  3. I check the error informs me of something remotely useful... or at least what the command thinks is useful
When I first tried to implement this negative test, it didn't work; naturally I had copy-pasted the unit-test and littered it with the async metadata-attribute and sequence stuff... no bueno.

But then it dawned on me that error detection i.e. for [Test(expect=...)], needed errors to occur in the same call-stack, so that when the stack unwinds, FlexUnit can use a try-catch block to detect and cross-check for the expect=... and make an assertion on the expected error type.

That's my unverified rationalization for why it didn't work, anyway, and for adopting a similar messaging approach as before, hence rethrowIt(...). I had previously thought the error was getting lost, but I knew there was a way to respond to them.

rethrowIt(...) is annotated with the [MessageError] metadata tag, which again, Parsley will detect during initialization and wire-up. So whenever there is an error in a [MessageHandler] or [CommandResult], Parsley will let the test know via rethrowIt(...).

Similar to passItOn(...), rethrowIt(...) uses the eventDispatcher to pass the error onto the Flex Event Mechanism; rethrowIt(...) is a little bit different in that it must marshal the Error object into a form the Flex Event Mechanism can understand, that is, an event and specifically a ErrorEvent instance.

What's Next...

Part 3: Hiding Fluint Sequences with a Flow-based DSL
I'll introduce the AsyncHelper base class and leverage FlexUnit's [Before] and [After] inheritance to abstract away all that Fluint Sequence stuff.

This approach makes available a bunch of helper methods that make asynchronous tests much easier to read.

Monday, April 11, 2011

Darren on Flex: FlexUnit4 Testing with Parsley - Unit Testing Command Objects

In Part I, I'll start off by implementing a 'purist' unit-test. In order to do this, however, I need a context; I think a noddy CRM system will do - I was tempted to use a Calculator system, but decided against it as there was too much temptation to actually implement some logic.
Ironically, the point of this blog series is not about testing logic :-/

CRM It Is Then...

As mentioned in the intro, I've been working with Cairngorm3 and typically testing Parsley Command objects and Cairngorm-style PresentationModels (PMs).

After applying the Cairngorm3 guidelines to the CRM system, I've structured the project as illustrated.

At the application-layer there is the:
  • DirectoryService interface, which will be stubbed and later used by Parsley to injection-by-type
  • GetPersonCommand, which in this exercise will be the unit-under-test (UUT)
  • and PersonEvent, which will eventually be used with Parsley's Messaging Framework to implement low-coupling.
In the domain-layer there's the lonely Person class; I'll be making assertions against this and at some point during the series I'll demonstrate two neat ways to build and leverage test-fixtures.

The 'main' Code...

First up is the DirectoryService interface
package com.darrenbishop.crm.directory.application {
    import mx.rpc.AsyncToken;
    
    public interface DirectoryService {
        function lookupById(id:uint):AsyncToken;
    }
}
Bit of a crappy CRM, with its one service call, but remember this is about testing the Command object, not service logic.
Next is the aforementioned UUT, the GetPersonCommand
This is a standard Command adhering to the execute-result-error method naming convention. These methods will be auto-detected by Parsley if the command is declared as a DynamicCommand; I'll use the [Metadata] tags when it comes to it:
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.domain.Person;
    
    import mx.collections.ArrayCollection;
    import mx.rpc.AsyncToken;
    import mx.rpc.Fault;

    public class GetPersonCommand {
        public var dispatcher:Function;
        
        public var service:DirectoryService;
        
        public function execute(event:PersonEvent):AsyncToken {
            return service.lookupById(event.id);
        }
        
        public function result(person:Person, event:PersonEvent):void {
            if (person.id != event.id) {
                throw new Error(sprintf('Found person (%d) does not match lookup person (%d)', person.id, event.id));
            }
            dispatcher(PersonEvent.newFound(person));
        }
        
        public function error(f:Fault, event:PersonEvent):void {
        }
    }
}
You can see the execute method is a simple pass-through i.e. there is no outbound data-translation or encoding, etc. before the service call.

The result method doesn't do any inbound data-translation, etc. but I do introduce a bit of verification logic: where the event parameter is passed the original triggering event i.e. the same object received by the execute method, I have access to the id of the person requested; the person parameter is passed the person looked-up and found by the would-be CRM system's directory-service. I use this data to verify I have actually found the person I am looking for... put another way, I've contrived a situation where I can throw an error; you'll see I will force this situation to implement a negative-test.

Last but not least, the PersonEvent
Events are re-purposed in Parsley as messages in its Messaging Framework - broadly doing the same thing as the standard event-mechanism, but offering better de-coupling, implementing a topic-subscription approach with message selectors.
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.domain.Person;
    
    import flash.events.Event;

    public class PersonEvent extends Event {
        public static const LOOKUP:String = 'PersonEvent.lookup';
        
        public static const FOUND:String = 'PersonEvent.found';
        
        private var _id:uint;
        
        public function get id():uint {
            return _id;
        }
        
        private var _person:Person;
        
        public function get person():Person {
            return _person;
        }
        
        function PersonEvent(_:Guard, type:String) {
            super(type);
        }
        
        public static function newLookup(id:uint):PersonEvent {
            var event:PersonEvent = new PersonEvent(Guard.IT, LOOKUP);
            event._id = id;
            return event;
        }

        public static function newFound(person:Person):PersonEvent {
            var event:PersonEvent = new PersonEvent(Guard.IT, FOUND);
            event._person = person;
            return event;
        }
        
        public override function clone():Event {
            var event:PersonEvent = new PersonEvent(Guard.IT, type);
            event._id = id;
            event._person = person;
            return event;
        }
    }
}

class Guard {
    internal static const IT:Guard = new Guard();
}
I use static factory methods here in-lieu of constructor-overloading - better anyway for removing confusion over what events are being constructed with what arguments. To restrict access to the sole constructor, I use a slight variation of the internal-Sentinel pattern that's quite common in Flex.

Anyway, that's it for the 'main' code, on to...

The 'test' Code...

I'll start with the actual test class
The FlexUnit4 GetPersonCommandUnitTest
This is a unit-test in the purist sense; all I'm interested in here is the behaviour of the command object, specifically, what goes on in the execute-result-error methods. I feed the command canned-data and spy on it's outputs by controlling the objects it collaborates with. In this test case, those would be the service and dispatcher objects; the latter, which is a Function object, I use directly to make assertions, the former I inspect and assert on what I find.
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.domain.Person;
    import com.darrenbishop.support.create;
    
    import org.flexunit.assertThat;
    import org.flexunit.asserts.fail;
    import org.hamcrest.object.equalTo;
    
    public class GetPersonCommandUnitTest {
        public var service:StubLookupTrackingDirectoryService;
        public var command:GetPersonCommand;
        
        [Before]
        public function prepareCommand():void {
            service = new StubLookupTrackingDirectoryService();
            command = new GetPersonCommand();
            command.service = service;
        }
        
        [Test(description='Test GetPersonCommand calls the DirectoryService.lookupById(...) service correctly.')]
        public function lookUpByIdIsCalledCorrectly():void {
            service.add(create(Person, {'id': 001, 'firstname': 'John001', 'lastname': 'Smith001', 'phone': 6977150}));
            service.add(create(Person, {'id': 002, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168}));
            service.add(create(Person, {'id': 003, 'firstname': 'John003', 'lastname': 'Smith003', 'phone': 7208442}));
            
            command.execute(PersonEvent.newLookup(002));
            command.execute(PersonEvent.newLookup(001));
            command.execute(PersonEvent.newLookup(003));
            
            assertThat(service.nextLookup().fullname, equalTo('John002 Smith002'));
            assertThat(service.nextLookup().fullname, equalTo('John001 Smith001'));
            assertThat(service.nextLookup().fullname, equalTo('John003 Smith003'));
        }
        
        [Test(description='Test GetPersonCommand result-handler does not mangle the person found.')]
        public function resultDoesNotManglePersonFound():void {
            command.dispatcher = function(event:PersonEvent):void {
                assertThat(event.person.fullname, equalTo('John002 Smith002'));
            };
            
            var person:Person = create(Person, {'id': 002, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            command.result(person, PersonEvent.newLookup(002));
        }
        
        [Test(expects='Error',description='Test GetPersonCommand result-handler verifies the id of the person found.')]
        public function resultVerifiesPersonFound():void {
            command.dispatcher = function(event:PersonEvent):void {  };
            
            var person:Person = create(Person, {'id': 001, 'firstname': 'John002', 'lastname': 'Smith002', 'phone': 6809168});
            command.result(person, PersonEvent.newLookup(002));
        }
    }
}
I've implemented three tests, including the one negative test, that gives 75% branch coverage; testing the error method is just the same as testing the result method, so I've skipped it.

Some might wonder why I don't use mocking to stub/mock the DirectoryService - I really ought to have but the integration options of, say mockito-flex (my favourite), are not available to me: I can't extend MockitoTestCase or use the MockitoClassRunner as in the next parts I use both mechanisms to implement the Parsley integration and DSL support.

On that note, here's the code for the stubbed DirectoryService...

Faking It: The StubLookupTrackingDirectoryService
package com.darrenbishop.crm.directory.application {
    import com.darrenbishop.crm.directory.domain.Person;
    
    import flash.utils.Dictionary;
    
    import mx.rpc.AsyncToken;
    
    public class StubLookupTrackingDirectoryService implements DirectoryService {
        private var people:Dictionary;
        
        private var lookups:Array;
        
        public function StubLookupTrackingDirectoryService() {
            people = new Dictionary();
            resetLookups();
        }
        
        public function resetLookups():void {
            lookups = [];
        }
        
        public function add(person:Person):void {
            if (people[person.id]) {
                throw new Error('A person with id ' + person.id + ' already exists.');
            }
            
            people[person.id] = person;
        }
        
        public function nextLookup():Person {
            return lookups.shift();
        }
        
        public function lookupById(id:uint):AsyncToken {
            lookups.push(people[id]);
            return null;
        }
    }
}
You can see the stub has two variables:
  • people, a dictionary which keys Person objects against their id
  • lookups, which is used to track the order in which the Command object makes person lookups... I know, it screams out for mocking, right
The people dictionary was originally populated with a dozen or so Person objects; I refactored this as having the test fixture combined into the stub isn't so good. I pushed it out to the test methods, which is a better approach, providing self documenting tests; from reading the test method I know exactly: what reference-data I'm priming the system with i.e. the Person objects; what test-data I'm activating the system with i.e. the id to lookup with; what output-data I expect i.e. the found person. Much better.

What's Next...

Part 2: Asynchronous Testing with Parsley and Fluint Sequences
I'll use FlexUnit4's [Before] feature to build a Parsley context and dynamically initialize the test class with references to the object-under-test (OUT) and the OUT's dependencies.

I'll also show using Fluint Sequences to test a complete asynchronous flow through the Command object.

Tuesday, April 05, 2011

Darren on Flex: FlexUnit4 Testing with Parsley

I've explored in the past the features of FlexUnit4, specifically its asynchronous and UI capabilities, to test the behaviour of the mixins over at my Flexins Project. Recently, however, I've been using Cairngorm and Parsley on an assignment and I've channelled my efforts into testing UI-logic. I've deferred testing views for the time being, focusing on Parsley Command objects and Cairngorm-style presentation-models (PMs) - typically objects containing logic that is triggered by the Parsley Messaging framework.

I am using FlexUnit, but I recognise some might classify this testing as something other than unit-testing; calling it integration-testing seems a bit grand, seeing as for a Flex application, being traditionally client-server, I'm not integrating anything. I'd rather call it component-testing, looking at it from the point of view that I target the collaboration of a small subset of classes that support one functional area of the application.

Still, by saying that, what I really want to acknowledge is that there maybe better tools for this kind of testing. For example I am a fan of Behaviour Driven Development and have also explored using Cucumber, FunFX and Melomel.

This blog series is really just an account of what I have achieved in proving the concept of Parsley supporting FlexUnit testing.... of Cairngorm stuff.

Where I am a fan of DSLs, I will also show you how an embedded DSL can be used to introduce a bit of flow to your tests and abstract away details that gradually starts to make my head spin.

I re-iterate, it's all just a display of what can be done - it's by no means polished and I'm not even claiming that it's a good approach - it's simply a workable approach to shake-down your message wiring and interactions between Parsley-managed objects.

  1. Unit Testing Command Objects
  2. Asynchronous Testing with Parsley and Fluint Sequences
  3. Hiding Fluint Sequences with a Flow-based DSL
  4. Improved Parsley Support with FlexUnit's [RunWith(...)][Rule]
  5. Testing with a Parsley-Aware DSL
  6. Testing with Mock Injections
I hope you find it useful.

Update:

FlexUnit 4.1 has been released, providing new features including the handy [Rule] hook-mechanism

Sunday, September 21, 2008

Darren on Flex: Overloading the Constructor

Prologue

Back on the 3rd September 2008 some of my colleagues at Zuhlke unvailed StuffPlanner at a BCS SPA evening talk. StuffPlanner is an Agile estimation and planning tool with an opinion. Recently, I found myself ramping up my involvement in the development of StuffPlanner. Consequently, I have resumed my study and assessment of the technologies involved and their corresponding conventions and recommended usage. Henceforth, I blog briefly on the topic of constructor overloading.

Wait! ActionScript Does Not Support Overloading!

No, it doesn't... but please, keep reading. Sometime this week I received a communicade from a local colleague who is doing some pretty interesting work on the continent on the Flex platform. He asked me how to, or rather can you, overload a constructor. We (the locals) had (less) recently had a debate about this after realising that AS3 (ActionScript 3) does not support overloading, so I was primed to make a suitable response.

Default Parameters

Basically, in lieu of overloading AS3 gives you default parameters
public function DefaultValueExample(required:String, andTheCommonCase:String="DEFAULT") {
    if (!required) {
        throw new ArgumentError("You must specify required arguments");
    }

    // construct something with the required value andTheCommonCase
} 
Obviously the API designer needs to know the complete set of parameters, that is, those that must be specified and those that can have a default values. So far my references to the common-case are just to the concept of it, a place holder. The common-case represents the design challenge in using this language feature while, simultaneously, it represents the design limitation; the common-case(s) is prescribed to all the API clients irrespective of client needs. However, in practice the common-case is not a difficult trade-off to figure out and tends to be only a minor inconvenience. For example, in my experience, such cases tend to crop up during unit testing, which I anticipate will be less of a problem with TDD (yes, I am still homing my skills with TDD on Flex/AS3).

Variable Arguments

It may also be useful to use varargs to replace constructor overloading.
public function VarArgExample(... args) {
    for each (var arg:* in args) {
        // process arg...
    }
    //... then do construction
}
I reckon this has less application than default parameters as it only makes-sense/works when the (signature of the) things you are using during construction have parameters of no-type (* or Object) or a common super-type. Then you can either leverage duck-typing or polymorphism, respectively, to do smart things, which I won't discuss here.

The Factory Pattern

Oh yes, that old gem! I am a growing fan of using intention revealing names for variables and methods. It is perhaps a more common/old practice for variables (although you'd be surprised, even today), where variables are named to reveal the state it represents and/or for what it could be used to achieve. With methods, on the other hand, I sense a stigma, a taboo, in using this technique in any progressive way. Thus I feel there is a general hesitance to use this practice, as if it would imply some mediocrity in the coder's skill at writing good code. I do feel TDD development has gone some way to alleviate this, specifically, where test method names describe the example situation being tested; this some how seems more socially acceptable. And, while I'm at it, I'd like to suggest that this technique encourages you to write more cohesive methods w.r.t. what an object's methods do to contribute to the object's overall task. The reason I discuss this here (I did not plan to get into it that much) is because AS3 does not allow you to reuse method names i.e. no overloading. So rather than settling for less, why not combine intention revealing names with the Factory pattern. Now I have said it, I bet you are realizing you do this all the time (I anticipate) during testing, where you might have refactored some creational code for some objects used commonly in your tests. So what if we drop the usual XxxFactory's getInstance(), newInstances() and createInstance() cruft and adopt something more revealing?
//Person.as
public class Person {
    public function Person(firstName:String="", lastName:String="", ... middleNames) {
        // do somthing
    }  
}

//PersonFactory.as
public class PersonFactory {
    public function newPersonWithFirstName(firstname:String):Person { return new Person(firstname); }
    public function newPersonWithLastName(lastname:String):Person { return new Person(null, lastname); } // A little inconvenient
    public function newPersonWithFirstAndLastName(firstname:String, lastname:String):Person { return new Person(firstname, lastname); }
}
So what's the runtime overhead? Just the extra PersonFactory class, where the would-be constructor overloads are replaced with simple methods; let's face it, negligible. And the API overhead? Well interestingly, a problem has emerged and a pattern has been used to solve it. Furthermore, our intention revealing names should improve readability of the code and identifies a good place to refactor/relocate creational code that gets used over and over again... like, for instance, in your tests. Comments welcome.