Test Persona – Persona focused testing

September 26, 2011

In my last post I introduced the concept of a Test Persona – a software pattern for building acceptance tests that encapsulate the activities of an indivdual user of the system.  I showed that acceptance tests could be refactored so that step definitions are defined in terms of actors in the system, rather than imperative bullet points.  In this post, I introduce the pattern of Persona Focussed Testing.

Test Personas can be composed of strategies

Consider using test personas to trigger conversations around the actions and experiences of users that might not be otherwise be easily be apparent when implementing the tests.  For instance, we could rewrite the acceptance criteria from the first blog:

   Given I am a price-conscious customer
   When I purchase an item that is sourced from my country
   Then I will have chosen the 'free delivery' delivery option
   And I will have paid $0.00 for delivery

The difference here is that we are simulating user behaviour, rather than specifying the appearance or process for purchasing/choosing delivery options.

The implementation in mapping might be rewritten as such:

   /I am a price-consious customer/
      me = create_customer :item_selection_policy => :cheapest 
   /I purchase an item that is sourced from my country/
      my.desired_item = find_item :stock_location => my.location
      i.purchase_item  #### Calling this makes the persona follow a purchase through to completion.
   /I will have chosen the 'free delivery' delivery option/
      my.purchases.last.delivery_option = 'free delivery' 
   /I will have paid $0.00 for delivery/
      my.purchases.last.delivery_charge.should.be 0.00

In the above example, the Customer test persona maintains a list of that persona’s own purchases.

Test Personas have agency, and access their own assets

If your Test Personas have email addresses, it might be useful to have the persona provide easy access to the email messages, e.g.: they have received:

    my.inbox.should.contain {| msg | 
 msg.subject=="Receipt #${my.purchases.last.receipt_number}" 
 }

The idea here is to only expose access to resources that a given persona has.  For instance, I wouldn’t expect a customer to have access to, say, the inventory system, or the shipping manifests.  But emails are definitely things a customer would have access to, along with their fax machine, their delivery address, their credit card, etc.

In my next post, I’ll talk about some tentative uses of test personas in defining a good customer experience.


Test Persona: a pattern for acceptance tests

September 25, 2011

Test Persona is a software pattern for acceptance tests. The use of this pattern leads to a de-coupling of test motivation from test implementation. Using test personas in acceptance tests is part of building a rich test domain. This pattern is complementary to other patterns that focus on the test domain, such as the page object pattern.

Concept

A test persona has knowledge of the actions and information available to a particular actor in a system under test.  Acceptance tests interact with the test persons to accomplish activities that would normally be performed

Example

Here is a typical gherkin acceptance test

Given I am a customer
When I am about to purchase an item that can be sourced in my country
Then the cheapest price should include 'free delivery' as a delivery option

Most natural language based test harnesses will require a developer to implement a mapping into structured code. For instance, using page objects and some implicit web steps

/I am a customer/
  visit CustomerHomePage
/I am about to purchase an item that can be sourced in my country/
  @item = find_item :stock_location => 'australia'
  visit url_for item :action => :purchase
/I should see 'free delivery' as an available delivery option/
  on_page(PurchasePage).delivery_options.
    should.contain :text => 'free delivery'

Once the persona pattern is applied, we see that the actual user is more explicit, but interactions with data are less so.

before_scenario do
  attr i
  Alias_method me, my, :i
/I am a customer/
  me= create_customer
/I am about to purchase an item that can be sourced from my country/
  my.desired_item = find_item :stock_location => my.location
  i.start_to_purchase_item
/I should see 'free delivery' as an available delivery option/
  my.purchase_page.delivery_options.
    should contain :text => 'free delivery'

In this refactored example, the persona (returned using create_customer) holds many of the methods that were previously implicit in the world class.

In my next post, I’ll discuss some of the patterns of use in persona-focussed acceptance testing


What else I learned about powershell this week

September 16, 2011

Remoting.  Windows Remoting.  Powershell.  And variables.  With functions….

When you use powershell to connect to remote machines, you have to worry about where and how variables gets defined.

$s = New-PSSession -Computer localhost
Invoke-Command -Session $s -ArgList 1 -ScriptBlock { param($number) $number }

Yep. That’s what it takes to define a local variable in a script block

Today, I found that the powershell session has it’s own scope that lasts as long as the powershell scope.

 Invoke-Command -Session $s -ArgList "my_value" "value" -ScriptBlock { 
           param($name, $value)
           Set-Item Variable:$name = $value
      }
      Invoke-Command -Session $s -ScriptBlock { $my_value }

This will set a variable on a session in one remote invocation, and retrieve it on another. Crazy!  Or rather, just like an SSH session.

So I now want to make use of this to push local variables from my script over to a remote machine. Something like:

$wibble = "hi"
OnRemote -server "localhost" -locals "wibble" -action {
   Write-Host "Hi I'm on remote machine with $wibble == hi"
}

What the devil should OnRemote look like? Here’s the signature…

function OnRemote() {
    param($server, $locals=@(), [scriptblock]$action)
}

…and the body…

      $s = New-PSSession -Computer $server
      @($locals) | % { Invoke-Command -Session $s -ArgList $_ Variable:$_ -ScriptBlock {
           param($name, $value)
           New-Item -name Variable:$name -value $value
      }
      Invoke-Command -Session $s -ScriptBlock $action
      End-Session $s

At the moment I’ve haven’t gone any further with this, and I’m sure there’s a way of binding variables into the remote context more consisely. As it is, you have to remember to keep your locals declaration in sync with the values being pushed.

If I were to go to the next step, I’d probably try to remove that need to keep local names in sync. I guess it would look something like this:

function MyFunction(){
   $remote:wibble = 5 # or [remote]$wibble=5
   OnRemote {
       Write-Host "The value $wibble comes from the host"
   }

What I’ve learnt about Powershell this week

September 10, 2011

Much to my suprise, Powershell holds up as a decent, testable language, with a few idiosyncrasies.

So far, I’ve been able to refactor Powershell to make use of its pervasive stream handling, and there are some useful tricks that can make Powershell look like other functional-style languages you know and love ( ruby, scala, LINQ, etc).

  • There are some nifty aliases:
     ? { test } |
    % { process; result } |
    % { process_result; finish}

    ? is an alias for ‘where-object’
    % in an alias of for-each.

  • More syntactic strangeness
     @{}

    creates an empty hashmap, but

    @[]

    wraps things into a list context. You’ll notice that when the following expression doesn’t work like you’d expect:

    (gci -path $path -filter "*.exe").Count

    this does:

    @[(gci -path $path -filter "*.exe")].Count
  • There is no built-in inject/foldl/aggregate function:
    function Fold-Left
    {
      [cmdletbinding()]
      param (
        [parameter(mandatory, position = 1)]$inital,
        [parameter(mandatory, position = 2)][scriptblock]$action 
      )
      begin { $sum = $inital}
      process {$sum = & $action $sum}
      end { write-output $sum }
    }

    We can use it to reduce intermediate variables. This will return a map containing symbol counts:

    $counts = a,a,a,a,b,c,d,e | 
       Fold-Left @{} { param($counts) $counts.$_ = $counts.$_ + 1}
    
      #$counts @{ "a" => 4; "b" => 1; .... }
  • For-each collects the return values of the for-each block.
    (1,2,3 | % { $_ + 1}) -eq @[2,3,4]

    However, if you want to modify items in place, it can be error-prone to remember the return value:

    @{"ting" = "hi"}, @{"thing" = "lo"} |
    % { $_.tong = $_.ting + "there"; $_} #note the return value
  • Tap is a useful function, here it is
    function Tap
    {
      [cmdletbinding()] 
      param([parameter(position=1)][scriptblock]$action)
      process {&action; write-output $_}
    }
    
    @{"ting" = "hi"}, @{"thing" = "lo"} | Tap { $_.tong = $_.ting + "there"} #note the return value is now ignored.

    This is a kestrel, for introducing side-effects. If your style is towards immutability you might want to use it differently.
    For instance, it’s great for logging:

    "command1", "command2" | Tap { Write-Host $_ } | Invoke-Expression $_

    And for turning it off, use…

    function Dont() {
      [cmdletbinding()] 
      param([parameter(position=1)][scriptblock]$action)
      process {write-output $_}
    }
    
    "command1", "command2" | Dont { Write-Host $_ } | Invoke-Expression $_