Do iteration 1 first

Sounds like a truism, right?

But not if you do an iteration 0, as I advocated previously.

“Iteration 0” is often used to build up the development environment, choose an architecture, do planning, and general preparatory work.  It’s a good thing, and can save you tons of time later.  It’s best summed up in the phrase “getting off to a good start“.

There is one thing more important than getting a good start.  That’s getting started.  I’ve found myself in paralysis over the hurdles involved in setting up an environment, establishing a process, building infrastructure, etc.  And I actually like doing that stuff.  It’s my specialty.

I found there was nothing more liberating than scrapping it all– design, testing, version control, integration, environments, processes —  and just coding.  The instigation was having something to show, a prototype.  I actually even got into doing a little UI design, messing with fonts and spacing and things like that (a well known pitfall & tarpit.)

You know what? It got me stoked.  It gave me the psychological attachment I needed.  There’s nothing like building a little momentum.

I  found a bug, pulled out my hair, and did it again when I reintroduced it.  If only I’d written an automated regression test to make sure that didn’t happen.  I even lost my source once because I didn’t check in and my IDE crashed.

So what? It’s only iteration 1.  Explore!  Make mistakes!  Make big mistakes!!  Correct them, start over, do whatever you want.  Heck, I had so much fun, and things are rolling so good, I might just move on to iteration 2.

Since we’re dealing with software, having to go back isn’t such a big deal.  There are no wasted materials (only time), and it’s not like if you set sail over the horizon without a life raft.  You can make changes on the fly, particularly early.  Setting up a big framework and process too early can introduce too much rigidity, too much friction.

I guess what I’m advocating, is adding your process, your environment, your QA and all those things iteratively too.  Include bits as part of each iteration.  For instance:  after losing my source, I went back to using version control.  Luckily it was already in place, so it was just a matter of choosing to use it.  No creating a repository and building an environment to host it.

I could have written a bug and a regression on the first bug, but I’ll let it slide.  When the next one bites me, I’ll log into Bugzilla and write it down (luckily, that’s already set up too.)  If it’s UI related, I might write some automation exposing it.

When I start to see a need for refactoring (soon), I’ll add some unit tests.  Since iteration 1 is almost over, I’m going to document my stories post-facto.  That way I’ll know that I’m complete because I’ll detail my acceptance criteria.  Maybe for iteration 2 I’ll write them down first.

When I get tired of typing ‘rake test’, I’ll work on continuous integration.  I’m only checking in once a day or so now (and there’s only me) so, it’s probably a premature optimization.  I just created a test deployment directory that I can manually scp to in mean time.

Check out my progress so far:

http://taskboard.qa-site.com/deploy/0.1/A/taskboard.swf

http://taskboard.qa-site.com/deploy/0.1/B/taskboard.swf

http://taskboard.qa-site.com/deploy/0.1/C/taskboard.swf

With a few more touches, I’ll put a bow on it and call it iteration 1.

Using FlexBuilder and Sprouts together

I decided to try to create a Sprouts project over top of my existing FlexBuilder project; the purpose being to use the test generators and framework that comes with Sprouts.

First, I opened a shell and went to my FlexBuilder workspace directory:

    cd c:\dev\flex\FlexBuilder\workspace

I found my workspace location by right-clicking on my FlexBuilder project.
project-properties

Then I threw caution to the wind and just created a sprouts project with the same over the top of it (my project is under version control, isn’t yours?)*

* while my project is under source control, I’m still not happy with the way Eclipse (FlexBuilder) imports the project.  FlexBuilder makes too many assumptions about file locations, and you might have to end up hacking your .project file to get everything smoothe.  I’d like to learn more about how others are handling importing projects from svn into FlexBuilder, and may have a future post discussing it.
    C:\dev\flex\FlexBuilder\workspace>sprout -n as3 taskboard-flex
    >> Creating new project 'taskboard-flex' with as3
          exists  taskboard-flex
          create  taskboard-flex/assets/skins/taskboard-flex
          create  taskboard-flex/bin
          create  taskboard-flex/lib
          create  taskboard-flex/script
          exists  taskboard-flex/src
          exists  taskboard-flex/test
          create  taskboard-flex/assets/skins/taskboard-flex/ProjectSprouts.png
          create  taskboard-flex/rakefile.rb
          create  taskboard-flex/README.txt
          create  taskboard-flex/script/generate.rb
          create  taskboard-flex/assets/skins/taskboard-flexSkin.as
          create  taskboard-flex/src/taskboard-flex.as
          create  taskboard-flex/src/taskboard-flexRunner.as
          create  taskboard-flex/src/taskboard-flexXMLRunner.as

Doesn’t look like it did any harm.

Notice that Sprouts (Sprout?) detected several folders already exist and doesn’t complain about it.  The project folder (taskboard-flex/) and src/ folder were created by FlexBuilder.  The test folder has existing tests, but I don’t have a satisfactory way to run them.  (Perhaps an AsUnit runner inside Eclipse would make a good first Eclipse Plugin project someday.)

Some of the generated objects, such as taskboard-flex.as aren’t needed and can be deleted.  We’ll leave them for now, since they’re not doing any harm.  My application is run from taskboard.mxml anyway.  If it had the same name however, (e.g., taskboard-flex.mxml) that could pose namespace problems.

Now I can create a test case using Sprouts:

    C:\dev\flex\FlexBuilder\workspace>cd taskboard-flex
    C:\dev\flex\FlexBuilder\workspace\taskboard-flex>script\generate.rb test taskboard.model.Task
asunit3.2.8.zip:   100% |....................|  26.7KB 342.4KB/s Time: 00:00:00
flex_sdk_3.zip:    100% |....................| 121.2MB 417.5KB/s Time: 00:04:57
[WARNING] symlink() function is unimplemented on this machine for: runtimes/air/mac/Adobe AIR.framework/Resources
[WARNING] symlink() function is unimplemented on this machine for: runtimes/air/mac/Adobe AIR.framework/Versions/Current
[WARNING] symlink() function is unimplemented on this machine for: runtimes/air/mac/Adobe AIR.framework/Adobe AIR
      create  test/taskboard/model
      create  test/taskboard/model/TaskTest.as
       force  test/AllTests.as

I don’t know what those warnings about symlink are about, but we shouldn’t let that worry us.  c’est Windows!

The ‘force’ on test/AllTests.as turns out to be benign too, despite the ominous comment.  My existing test (SampleTest) is still there.

    package {
        /**
         * This file has been automatically created using
         * #!/usr/bin/ruby script/generate suite
         * If you modify it and run this script, your
         * modifications will be lost!
         */

        import asunit.framework.TestSuite;
        import SampleTest;
        import taskboard.model.TaskTest;

        public class AllTests extends TestSuite {

            public function AllTests() {
                addTest(new SampleTest());
                addTest(new taskboard.model.TaskTest());
            }
        }
    }

I added the test folder to the build path and then noticed this problem in AllTests.as:

    1017: The definition of base class TestSuite was not found.    taskboard-flex/test    AllTests.as    line 13    1240423986281    655

It seems that it can’t find AsUnit in the src path, even though it was also added:

project_build_path1

When running from Sprouts this isn’t a problem because it downloaded both AsUnit and the Flex SDK and added them to the lib/ folder in the project.  (I think I’d like to see a user-level or even system-level cache of these libraries like the .m2/ folder for maven, though I realize this might present problems with multible versions for multiple projects.)

Let’s run the test and see what happens:

    C:\dev\flex\FlexBuilder\workspace\taskboard-flex>rake test
    (in C:/dev/flex/FlexBuilder/workspace/taskboard-flex)
    >> Execute: mxmlc.exe -debug -default-background-color=#FFFFFF -default-frame-rate=24 -default-size 900 550 -ou
    tput=bin/taskboard-flexRunner.swf -source-path+=src -source-path+=assets -source-path+=test -source-path+=lib\a
    sunit3 -verbose-stacktraces=true -warnings=true src/taskboard-flexRunner.as
    Loading configuration file C:\dev\flex\FlexBuilder\workspace\taskboard-flex\Sprouts\cache.7\sprout-flex3sdk-t
    ool-3.3.0\archive\frameworks\flex-config.xml

    rake aborted!
    [ERROR] C:\dev\flex\FlexBuilder\workspace\taskboard-flex\src\taskboard-flexRunner.as(4): col: 24 Error: Syntax
    error: expecting leftbrace before minus.

        public class taskboard-flexRunner extends TestRunner {
                      ^

    C:\dev\flex\FlexBuilder\workspace\taskboard-flex\src\taskboard-flexRunner.as(6): col: 28 Error: Syntax error: e
    xpecting leftparen before minus.

            public function taskboard-flexRunner() {
                         ^

    (See full trace by running task with --trace)

Okay, a simple problem.  Apparently, since it uses the project name for naming functions and classes, you can’t have a hyphen in a project.  Small problem.  Workaround is to rename the the project, or rename the file, class, and function.  Unfortunately, Flexbuilder can’t handle refactoring and I end up with functions like ‘taskboardFlexRunner-flexRunner’

After renaming the Runner files, classes, and functions, it still gives me this complaint:

    rake aborted!
    [ERROR] Error: unable to open 'src/taskboard-flexRunner.as'

So I still have problems.  Is the expected runner name hard-coded (dynamically generated based on project name) somewhere?

I’ll give up for that and just check out again as a new project named ‘taskboard’ (without the ‘-flex’) and worry about conflicting with my non-flex taskboard project later.

Then create a sprouts project on top of that:

    C:\dev\flex\FlexBuilder\workspace>sprout -n as3 taskboard
    >> Creating new project 'taskboard' with as3
          exists  taskboard
          create  taskboard/assets/skins/taskboard
          create  taskboard/bin
          create  taskboard/lib
          create  taskboard/script
          exists  taskboard/src
          create  taskboard/test
          create  taskboard/assets/skins/taskboard/ProjectSprouts.png
          create  taskboard/rakefile.rb
          create  taskboard/README.txt
          create  taskboard/script/generate.rb
          create  taskboard/assets/skins/taskboardSkin.as
          create  taskboard/src/taskboard.as
          create  taskboard/src/taskboardRunner.as
          create  taskboard/src/taskboardXMLRunner.as

And generate a test (note how it downloads the libraries again):

    C:\dev\flex\FlexBuilder\workspace\taskboard>script\generate.rb test taskboard.model.Project
    asunit3.2.8.zip:   100% |....................|  26.7KB 142.8KB/s Time: 00:00:00
    flex_sdk_3.zip:    100% |....................| 121.2MB 421.6KB/s Time: 00:04:54
    [WARNING] symlink() function is unimplemented on this machine for: runtimes/air/mac/Adobe AIR.framework/Resourc
    es
    [WARNING] symlink() function is unimplemented on this machine for: runtimes/air/mac/Adobe AIR.framework/Version
    s/Current
    [WARNING] symlink() function is unimplemented on this machine for: runtimes/air/mac/Adobe AIR.framework/Adobe A
    IR
          create  test/taskboard/model
          create  test/taskboard/model/ProjectTest.as
          create  test/AllTests.as

C:\dev\flex\FlexBuilder\workspace\taskboard>rake test
    (in C:/dev/flex/FlexBuilder/workspace/taskboard)
    >> Execute: mxmlc.exe -debug -default-background-color=#FFFFFF -default-frame-rate=24 -default-size 900 550 -ou
    tput=bin/taskboardRunner.swf -source-path+=src -source-path+=assets -source-path+=test -source-path+=lib\asunit
    3 -verbose-stacktraces=true -warnings=true src/taskboardRunner.as
    Loading configuration file C:\dev\flex\FlexBuilder\workspace\taskboard\Sprouts\cache.7\sprout-flex3sdk-tool-3
    .3.0\archive\frameworks\flex-config.xml
    C:\dev\flex\FlexBuilder\workspace\taskboard\bin\taskboardRunner.swf (90088 bytes)

    [WARNING] FlashPlayer encountered an error working with the mm.cfg log and/or editing the Trust file
    sa_flashplayer_9_: 100% |....................|   3.6MB 451.7KB/s Time: 00:00:08
    The MD5 Sum of the downloaded file (ae1da333575b308ffa5acae9e157512f) does not match what was expected (4135263
    488d95f41b3a846f7be288ad4).
    Would you like to install anyway? [Yn]
    y
    rake aborted!
    can't convert nil into String

    (See full trace by running task with --trace)

It still launches the flashplayer, but seems to re-download it every time. But I see my failing test:
asunit_failing_test

Correcting the deliberate failure by changing this line:

    public function testFailure():void {
        assertTrue("Failing test", false);
    }

to this:

    public function testPass():void {
        assertTrue("Passing test", true);
    }

and rerunning ‘rake test’ gives me the proverbial green :
asunit_passing_tests

I get this funny warning everytime I run it though:

    C:\dev\flex\FlexBuilder\workspace\taskboard>rake test
    (in C:/dev/flex/FlexBuilder/workspace/taskboard)
    >> Execute: mxmlc.exe -debug -default-background-color=#FFFFFF -default-frame-rate=24 -default-size 900 550 -ou
    tput=bin/taskboardRunner.swf -source-path+=src -source-path+=assets -source-path+=test -source-path+=lib\asunit
    3 -verbose-stacktraces=true -warnings=true src/taskboardRunner.as
    Loading configuration file C:\dev\flex\FlexBuilder\workspace\taskboard\Sprouts\cache.7\sprout-flex3sdk-tool-3
    .3.0\archive\frameworks\flex-config.xml
    C:\dev\flex\FlexBuilder\workspace\taskboard\bin\taskboardRunner.swf (90061 bytes)

    [WARNING] FlashPlayer encountered an error working with the mm.cfg log and/or editing the Trust file
    rake aborted!
    can't convert nil into String

    (See full trace by running task with --trace)

Rerunning with trace flace:

    C:\dev\flex\FlexBuilder\workspace\taskboard>rake test --trace
    (in C:/dev/flex/FlexBuilder/workspace/taskboard)
    ** Invoke test (first_time)
    ** Invoke bin/taskboardRunner.swf (first_time, not_needed)
    ** Invoke asunit3 (first_time)
    ** Invoke lib/asunit3 (first_time, not_needed)
    ** Invoke lib/asunit3 (not_needed)
    ** Execute asunit3
    ** Invoke src/main.mxml (first_time, not_needed)
    ** Invoke src/taskboard (first_time, not_needed)
    ** Invoke src/taskboard/components (first_time, not_needed)
    ** Invoke src/taskboard/components/NoteCard.mxml (first_time, not_needed)
    ** Invoke src/taskboard/components/TaskNoteCard.mxml (first_time, not_needed)
    ** Invoke src/taskboard/controller (first_time, not_needed)
    ** Invoke src/taskboard/framework (first_time, not_needed)
    ** Invoke src/taskboard/framework/BaseObject.as (first_time, not_needed)
    ** Invoke src/taskboard/framework/ModelObject.as (first_time, not_needed)
    ** Invoke src/taskboard/framework/ServiceObject.as (first_time, not_needed)
    ** Invoke src/taskboard/model (first_time, not_needed)
    ** Invoke src/taskboard/model/Project.as (first_time, not_needed)
    ** Invoke src/taskboard/model/Story.as (first_time, not_needed)
    ** Invoke src/taskboard/model/Task.as (first_time, not_needed)
    ** Invoke src/taskboard/model/TaskList.as (first_time, not_needed)
    ** Invoke src/taskboard/service (first_time, not_needed)
    ** Invoke src/taskboard/stub (first_time, not_needed)
    ** Invoke src/taskboard/stub/DataLoader.as (first_time, not_needed)
    ** Invoke src/taskboard/view (first_time, not_needed)
    ** Invoke src/taskboard.as (first_time, not_needed)
    ** Invoke src/taskboard.mxml (first_time, not_needed)
    ** Invoke src/taskboardFlexRunner.mxml (first_time, not_needed)
    ** Invoke src/taskboardRunner.as (first_time, not_needed)
    ** Invoke src/taskboardXMLRunner.as (first_time, not_needed)
    ** Invoke assets/skins (first_time, not_needed)
    ** Invoke assets/skins/taskboard (first_time, not_needed)
    ** Invoke assets/skins/taskboard/ProjectSprouts.png (first_time, not_needed)
    ** Invoke assets/skins/taskboardSkin.as (first_time, not_needed)
    ** Invoke test/AllTests.as (first_time, not_needed)
    ** Invoke test/taskboard (first_time, not_needed)
    ** Invoke test/taskboard/model (first_time, not_needed)
    ** Invoke test/taskboard/model/ProjectTest.as (first_time, not_needed)
    ** Invoke test/taskboard/model/StoryTest.as (first_time, not_needed)
    ** Invoke test/taskboard/model/TaskTest.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/errors (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/errors/AbstractError.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/errors/AssertionFailedError.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/errors/ClassNotFoundError.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/errors/InstanceNotFoundError.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/errors/UnimplementedFeatureError.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/Assert.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/AsynchronousTestCase.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/AsynchronousTestCaseExample.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/AsyncOperation.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/RemotingTestCase.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/Test.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestCase.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestCaseExample.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestFailure.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestListener.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestMethod.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestResult.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/framework/TestSuite.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/runner (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/runner/BaseTestRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/runner/TestSuiteLoader.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/runner/Version.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/AirRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/FlexRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/FlexTestRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/ResultPrinter.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/TestRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/TestTime.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/textui/XMLResultPrinter.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/util (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/util/ArrayIterator.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/util/Iterator.as (first_time, not_needed)
    ** Invoke lib/asunit3/archive/asunit/util/Properties.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/errors (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/errors/AbstractError.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/errors/AssertionFailedError.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/errors/ClassNotFoundError.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/errors/InstanceNotFoundError.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/errors/UnimplementedFeatureError.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/Assert.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/AsynchronousTestCase.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/AsynchronousTestCaseExample.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/AsyncOperation.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/RemotingTestCase.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/Test.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestCase.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestCaseExample.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestFailure.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestListener.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestMethod.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestResult.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/framework/TestSuite.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/runner (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/runner/BaseTestRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/runner/TestSuiteLoader.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/runner/Version.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/AirRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/FlexRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/FlexTestRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/ResultPrinter.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/TestRunner.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/TestTime.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/textui/XMLResultPrinter.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/util (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/util/ArrayIterator.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/util/Iterator.as (first_time, not_needed)
    ** Invoke lib/asunit3/asunit/util/Properties.as (first_time, not_needed)
    ** Invoke run_bin/taskboardRunner.swf (first_time)
    ** Execute run_bin/taskboardRunner.swf
    [WARNING] FlashPlayer encountered an error working with the mm.cfg log and/or editing the Trust file
    rake aborted!
    can't convert nil into String
    c:/dev/ruby/lib/ruby/gems/1.8/gems/sprout-flashplayer-bundle-9.151.1/lib/sprout/tasks/flashplayer_task.rb:281:i
    n `exists?'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/sprout-flashplayer-bundle-9.151.1/lib/sprout/tasks/flashplayer_task.rb:281:i
    n `read_log'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/sprout-flashplayer-bundle-9.151.1/lib/sprout/tasks/flashplayer_task.rb:233:i
    n `execute'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:578:in `invoke_with_call_chain'
    c:/dev/ruby/lib/ruby/1.8/monitor.rb:242:in `synchronize'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:571:in `invoke_with_call_chain'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:588:in `invoke_prerequisites'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:585:in `each'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:585:in `invoke_prerequisites'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:577:in `invoke_with_call_chain'
    c:/dev/ruby/lib/ruby/1.8/monitor.rb:242:in `synchronize'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:571:in `invoke_with_call_chain'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:564:in `invoke'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:2027:in `invoke_task'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:2005:in `top_level'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:2005:in `each'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:2005:in `top_level'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:2044:in `standard_exception_handling'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:1999:in `top_level'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:1977:in `run'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:2044:in `standard_exception_handling'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/lib/rake.rb:1974:in `run'
    c:/dev/ruby/lib/ruby/gems/1.8/gems/rake-0.8.4/bin/rake:31
    c:/dev/ruby/bin/rake:16:in `load'
    c:/dev/ruby/bin/rake:16

Anyway, it works, if not gracefully, and I still need to execute ‘rake test’ from the command line.  That’s not optimal, but I can live with it.

I tried creating a TestRunner that looks like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="runTests()" layout="absolute">
    <mx:Script>
    <![CDATA[
        import asunit.textui.FlexRunner;
        import asunit.textui.TestRunner;

        var runner:FlexRunner;

        function runTests():void {
            runner = new FlexRunner();
            runner.start(AllTests, null, TestRunner.SHOW_TRACE);
        }

    ]]>
    </mx:Script>

</mx:Application>

And AllTests that looks like this:

package {
    import asunit.framework.TestSuite;
    import taskboard.model.ProjectTest;
    import taskboard.model.StoryTest;
    import taskboard.model.TaskTest;

    public class AllTests extends TestSuite {
        public function AllTests() {
            addTest(new taskboard.model.ProjectTest());
            addTest(new taskboard.model.StoryTest());
            addTest(new taskboard.model.TaskTest());
        }
    }
}

I’m still plagued by this problem:

    1017: The definition of base class TestSuite was not found.    taskboard/test    AllTests.as    line 14    1240434196265    683

even though FlexBuilder has no trouble resolving it for command completion.

Links: Unit Testing and Continous Integration with Flex and AsUnit

Just a bunch of links to tutorials on using AsUnit and continuous integration with Flex Projects:

A post on AsUnit by one of it’s creators,  Luke Baye’s:
http://asserttrue.com/articles/2006/03/10/AsUnit25

An example of a simple TestRunner mxml (AS2):
http://asserttrue.com/articles/2006/10/05/flex2-mxml-project-support-in-asunit

Luke’s post on continous integration:
http://lukebayes.blogspot.com/2005/11/continuous-integration-with-asunit.html

A good tutorial about using AsUnit (but with only a Flash testRunner):
http://www.insideria.com/2008/09/unit-testing-with-asunit.html

Another good tutorial about using AsUnit:
http://www.insideria.com/2008/05/anatomy-of-an-enterprise-flex-10.html

Discussion of one team’s unit test framework requirements:
http://www.eyefodder.com/blog/2006/06/unit_test_frameworks_for_as3_a.shtml

Weaknesses and strengths of FlexUnit and AsUnit:
http://www.eyefodder.com/blog/2006/07/flexunit_asunit_deathmatch_res.shtml

Story of their use of Continuous Integration:
http://www.eyefodder.com/blog/continuous_integration/
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl.shtml
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl_1.shtml
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl_2.shtml
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl_3.shtml
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl_4.shtml
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl_5.shtml
http://www.eyefodder.com/blog/2006/05/continuous_integration_with_fl_6.shtml

A flash-oriented tutorial, but with good AsUnit explanations:
http://marstonstudio.com/2007/07/28/asunit-testing-with-flash-cs3-and-actionscript-3/

Book Review: Java Power Tools

I cannot recommend this book too highly.

Java Power Tools, by John Ferguson Smart and published by O’Reilly is almost exactly the book I would have written, had I the capability.  It contains descriptions and examples using almost every tool I know of (and many I didn’t) for ensuring code quality.  It talks through the build, version control, continous integration, unit/functional/ui/performance testing, static analysis, and issue tracking processes in logical order and covers the major open source tools for each category.

The tools are covered in depth, with not only technical details but commentary, insightful opinions (clearly labeled as such), and sprinkled with common sense reality checks that everyone knows, but sometimes forgets when they find a tool that’s so good at what it does (like code coverage, for instance.)

In fact, I could not provide a better description of what it is I do.  The book reads almost as a blueprint for my job description, and if someone asked me “what services do you offer at One Shore?”, I could just hand them a copy of Java Power Tools (but then probably not get their business, since all they’d need to do is read the book.)

The one thing I would change — but “change” isn’t the right word, “do differently if I’d written it” is better –instead of going to so much breadth and depth– is picking certain tools and providing a demonstration, a step by step narrative example more in like with the Pragamatic Programmer series, showing a real project with specific tools and real implementations such as a build script, ci project, unit and functional test cases, deployment scripts, etc.

Also, I wouldn’t have limited it to just java tools, but realize you need a focus.  I have a power point presentation I’ve been working on that has been gradually morphing into an opinion essay that looks quite a bit like the outline of Java Power Tools, but lacks the focus and breath.

Having both the focus and the breadth of detail is quite an achivement, and I commend the author for pulling it off.  Besides the logical flow, the book also makes a great reference and you can flip right to the chapter or section on a specific tool and see how to integrate it into your process.

I found particularly helpful the areas on Eclipse integration.  It’s been a weak point of mine, and I admit to being an IDE weenie who prefers a simple text editor, while realizing the power of the IDE but just not being able to take full advantage of it myself.

Another favorite of mine is the Maven details.  I’ve cursed maven and never really understood it.  Sometimes it’s magic that just works great — when it works, and sometimes it feels like a straightjacket and a mystifying puzzle.  I skipped over most of it, but I’m hoping a more serious read will help me to learn to love (or hate a little less) this powerful tool.

One thing I would change (of course) is to have more information of testing tools.  I think a section on Selenium RC and integration with JUnit or TestNG is necessary, and mention of WATIR (or WATIJ, since this is Java) would be nice, as well as Canoo, for those who want to test the web API, but not the UI.

I actually found the book when I stumbled across a post on John’s blog about FindBugs.  When I saw he had a whole chapter about this tool (that I’d just discovered) I immediately went to Safari and made room on my bookshelf for it.  Immediately after that, I saw he had a whole section of CI servers, covering the 4 that I’m interested in (CruiseControl, Continuum, Luntbuild, and Hudson) and knew I’d found the book I wanted.  Actually, I remembered recently having read his review of them on JavaWorld as well.

Get the book, read it, and then call me to do your  build, deployment, and testing using the tools described in Java Power Tools (because I could really use the work.)

Maybe I’ll try to write a shorter booklet about PHP or Ruby (particularly rake and sprouts)  using Java Power Tools as a pattern, or work on that narrative that describe a project start to finish with emphasis on tools to help improve quality.

Using Sprouts with an existing project

I still don’t understand how to use sprouts with an existing project.  Not without a lot of work, that is.

I’m thinking now of using rake to create a build script (which could then use the sprouts rake tasks in my own rakefile)  Perhaps I’m trying to think of it it as a monolithic solution.  The development environment setup, the project and class + test generators, and the rake tasks are all separate things.

Here it is in my blog post “More on Sprouts” from August 12, 2008:

“sprouts is a build tool, a project generator, a dependency resolver/ package manager, and a download, installation, and configuration tool.”

“One thing Sprouts does is set up a Flex/ActionScript development environment.”

“The next thing Sprouts does is Build your application. It does this by wrapping the Flex SDK commands like ‘mxmlc’ into Rake tasks.”

“Sprouts then gets into other magic like creating a test harness…and setting up a project environment.”

The three areas are broken down into three different ruby tools.  Gems, Rake, and Rubigen.

Also:

“It mentions IDE integration and continuous integration”

though I don’t know the state of these.  I think Eclipse/Flexbuilder integration is only forthcoming and Cruisecontrol support is rudimentary.

I don’t know because I haven’t explored deeper however.  The place I’m at now is trying to bolt a fledgling project built in Flexbuilder into a CI framework (Cruisecontrol.RB) and use the rake tasks in my own rakefile.

Because my IDE setup is a bit different than sprouts expects, I’ll have to do some work to make it fit.  That’s the dependecy bug biting you there.  Sprouts gereation is fairly good at looking like the default Flexbuilder file structure, but things like debugging, debug standalone player, sdk version, etc will take some tweaking.  Ironically, it’s probably easier to modify Eclipse to match Sprouts configuration & tool versions, but that’s a moving target and not really a repeatable process.

Although having your development and build environments different could be a good way to catch dependency bugs, it can also be terrible to troubleshoot.  I don’t know if I want to sign up to the Sprouts version setup, because some clients might have specific requirements that would be difficult to match.  Maybe a configurable ingredients list would be the way to go, like my own maven repository — which is where I don’t want to go.