Totmato Sauce, Software, Human Variability

Malcolm Gladwell at TED 2004 describes the research of Howard Moscowitz for Campbell’s Soup / Prego.

The lessons learned from Howard beautifully correllate with Kathy Sierra’s Quantum Mechanics of Users

The Nutshell

If users are asked for their preference, they will likely tell you something very common and rather vanilla. When given the experience of the choices, say during a taste test, the aggregated results are NOT similar, and in fact group around a handful of common choices.

Design Investigation for Software

My take on this is that during Design Investigation for software – be it website, desktop application, even for print – if at all possible, build the DEMO of the choices involved and get the trial user into the experience and THEN ask the user what is their preference.

This approach in awareness of user preference, user experience, and human variability could take the design into the “I Rock” space of the passionate user.

http://headrush.typepad.com/creatingpassionateusers/2005/12/thequantummec.html#trackback

In Search Of …

Peter John Fitzgibbons (Gillies)

My father is John Cecil Joseph Gillies (John Cecil Gillies)

My grandparents :

Robert Cecil Gillies and Millie(Mildred?) Gillies were separated in 1940s.

Millie and Ted married in 1961. Both deceased in 1979.

My aunts :

Roberta, Virginia and Ivy are all living. Roberta is now in Windsor, NS, CA. Her husband Roger passed away serveral years ago (2000?).

I am looking for anyone who shares this ancestory. I would like to learn who you are.

I want you to know that John Gillies is terminally ill and will pass away sometime in the next 10 days.

You may email me at peter.fitzgibbons@gmail.com

Coverage (rcov 0.5.0.1) in Ruby on Rails 1.1.2

rcov 0.5.0.1: code coverage for Ruby released by Mauricio Fernandez contains all that is needed to produce code-coverage reporting in Rails.

Requirements

Ruby 1.8.4 (especially on Win32 requires updated libraries.)
Rails 1.1.2

Classes

There is only one file to add to your current rails app: lib/tasks/coverage.rake

[ruby]
require ‘rcov/rcovtask’

namespace :test do
Rcov::RcovTask.new do |t|
t.libs << “test”
t.test_files = FileList['test/unit/*.rb', 'test/functional/*.rb']
t.output_dir = ‘test/coverage’
t.verbose = true
end
end
[/ruby]

Rake -T

Here’s the new tasks with rake -T :

(in C:/radrailsworkspace/pimki/trunk)
[... snip standard tasks ...]
rake test:clobber_rcov         # Remove rcov products for rcov
rake test:rcov                 # Analyze code coverage with tests
[...]

The default taskname for the RcovTask instance is ‘rcov’. Rcov automatically creates the ‘clobber_’ task that is basically a rm_rf on the RcovTask#output_dir.

Cover it

Run rake test:rcov and see how much of your code is really tested!!

Report Coverage

The end of your rake run will contain a pretty-print text report of the rcov. Also, you can navigate to ./test/coverage/index.html to see the graphic report. See the docs for more info.

Single Fixtures declaration for Selenium on Rails

When testing out a complex website, replicating the |open|/selenium/setup?fixtures=x| line would be painful!

So, Add a const at the beginning of the setup_controller in Selenium Plugin at
./vendor/plugins/selenium_on_rails/lib/controllers/selenium_controller.rb:

[ruby]
MY_FIXTURES = “table_a, table_b, table_c”
[/ruby]

Also alter #setup as so :
[ruby]
def setup
unless params.has_key? :keep_session
reset_session
@session_wiped = true
end
if params[:fixtures] == “all“
fixtures = PM_FIXTURES
end
fixtures ||= params[:fixtures].to_s
@loaded_fixtures = load_fixtures fixtures
render :file => view_path(‘setup.rhtml’), :layout => layout_path
end
[/ruby]

New lines are 6-9, and the change on 10 from referencing params[] to the local var fixtures.

Then, use this in the selenese script :

|open|/selenium/setup?fixtures=all|

Isn’t that beautiful!??

Now I don’t have to endure copy-paste-age-break hell on my fixtures configuration.

Ahhh, Ruby is so nice!

1 – Strategy Pattern

The Strategy Pattern really resolves to “composition” in practice. This OO technique says to take components that could change at runtime and encapsulate them in their own objects. The master-object is then “composed” of instances of the dependent clasess.

Example:

[ruby]
class Duck
def fly
puts ‘Flap Flap’
end

def quack
puts ‘Quack!’
end
end

d = Duck.new
d.flap
d.quack
[/ruby]

Produces:

[text]
Flap Flap
Quack!
[/text]

So now handle the en-user request to have ducks with different flap :
[ruby]
class FlapWings
def fly
puts ‘Flap Flap’
end
end

class FlapSoaring
def fly
puts ‘Soaring in the wind’
end
end

class Duck
def initialize (flapInstance)
@flapDelegate = flapInstance
end
def changeFlap(flapInstance)
@flapDelegate = flapInstance
end
def fly
@flapDelegate.fly
end

def quack
puts ‘Quack!’
end
end

mallard = Duck.new(FlapWings.new)
mallard.fly
mallard.quack
mallard.changeFlap(FlapSoaring.new)
mallard.fly
[/ruby]

Now produces :
[text]
Flap Flap
Quack!
Soaring in the wind
[/text]

This example looks trivial. The technique is powerful. Behold that in Java/C#, this requires a lot of effort including Inheritance and Interfaces.

Nice that Ruby allows this run-time change through duck-typing!!

Unit Test WPF Window with NUnit

I will outline how to setup NUnit testing with WinFX 1.0 (Feb CTP) in order to UnitTest an Avalon Window.

What’s wrong

Points of technology that drive the details.

  • WPF windows must run on an STA Thread (Single-Apartment) MS Forums
  • NUnit (and TestDriven.net, using the NUnit API) run all tests with an MTA Thread. NUnit Developers Wiki: ExceptionsOnThreads
  • In order to generate and start an STA thread from within a running MTA, grasshopper, you must warm up your System.Threading fu and investigate Thread() and Thread.SetApartmentState. GeekNoise : Peter Provost

The recipe to success

  1. Create a new class in your test Assembly to hold the NUnitHelper namespace and class.

    [csharp]
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Security.Permissions;
    using System.Threading;
    using System.Text;

    namespace NUnitHelpers
    {
    class CrossThreadTestRunner
    {
    private ThreadStart userDelegate;
    private Exception lastException;

    public void RunSTA(ThreadStart userdelegate)
    {
    this.userDelegate = userdelegate;
    Thread t = new Thread(new ThreadStart(this.MultiThreadedWorker));
    t.SetApartmentState(ApartmentState.STA);
    t.Start();
    t.Join();

    if (lastException != null)
    ThrowExceptionPreservingStack(lastException);

    }

    public void Run(ThreadStart userdelegate)
    {
    this.userDelegate = userdelegate;
    Thread t = new Thread(new ThreadStart(this.MultiThreadedWorker));
    t.Start();
    t.Join();

    if (lastException != null)
    ThrowExceptionPreservingStack(lastException);

    }

    [ReflectionPermission(SecurityAction.Demand)]
    private void ThrowExceptionPreservingStack(Exception e)
    {
    FieldInfo remoteStackTraceString = typeof(Exception).GetField(“_remoteStackTraceString”,
    BindingFlags.Instance | BindingFlags.NonPublic);
    remoteStackTraceString.SetValue(e, e.StackTrace + Environment.NewLine);
    throw e;
    }

    private void MultiThreadedWorker()
    {
    try
    {
    userDelegate.Invoke();
    }
    catch (Exception e)
    {
    lastException = e;
    }
    }

    }
    }
    [/csharp]

    This is adapted from Peter Provost’s original code. First, I compressed his constructor/method calls into just the method call. This allows me the cleanest calling-code that I can get with minimal effort. Second, I duplicated Run() into RunSTA() in order to test STA threads that are executing the WPF Window.

  2. Now my calling class (the actual NUnit testing class) can use this new helper to correctly process WPF testing (handling Assert exceptions instead of raising UnhandledException error inside NUnit’s execution path.) :

    [csharp]
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Windows;
    using System.Windows.Automation;
    using System.Windows.Interop;
    using NUnit.Framework;

    namespace NunitTest
    {
    [TestFixture]
    public class Class1
    {
    NUnitHelpers.CrossThreadTestRunner runner = new NUnitHelpers.CrossThreadTestRunner();

    [Test]
    public void truthtest()
    {
    Assert.IsTrue(true);
    }

    [Test]
    public void falsetest()
    {
    Assert.IsTrue(false);
    }

    public static void TestAddressbookWorker()
    {
    Window ab = new AddressBookLib.Window1();
    ab.Show();
    ab.Close();
    ab = null;
    Assert.IsTrue(false, “Forced NUNit Failure”);

    }

    [Test]
    public void TestAddressBook()
    {
    runner.RunSTA(new ThreadStart(TestAddressbookWorker));
    }

    }
    }
    [/csharp]

    Here each test method requires a worker that contains the actual testing code. The calling test is simply delegating the worker method into the CrossThreadTestRunner instance. Using RunSTA() starts an STA thread and then the WPF window is started happily.

  3. NUnit Output!! (For those unbelieving)

[text]
—— Test started: Assembly: NunitTest.dll ——

TestCase ‘NunitTest.Class1.falsetest’ failed:
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\Class1.cs(26,0): at NunitTest.Class1.falsetest()

TestCase ‘NunitTest.Class1.TestAddressBook’ failed: Forced NUNit Failure
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\Class1.cs(35,0): at NunitTest.Class1.TestAddressbookWorker()
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\CrossThreadTestRunner.cs(55,0): at NUnitHelpers.CrossThreadTestRunner.MultiThreadedWorker()
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\CrossThreadTestRunner.cs(48,0): at NUnitHelpers.CrossThreadTestRunner.ThrowExceptionPreservingStack(Exception e)
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\CrossThreadTestRunner.cs(24,0): at NUnitHelpers.CrossThreadTestRunner.RunSTA(ThreadStart userdelegate)
C:\Visual Studio 2005\Projects\WinFX\AddressBook\NunitTest\Class1.cs(42,0): at NunitTest.Class1.TestAddressBook()

1 passed, 2 failed, 0 skipped, took 21.25 seconds.
[/text]