Phillip Trelford's Array

POKE 36879,255

Functional Style BDD Steps

    Could a BDD framework be implemented simply as a fold over a sequence of lines in a document?
    Or in the words of Apple:

This changes everything. Again

marsattacks

Or in the words of Gojko Azdic:

Like anything else in F#, the primary use of this feature is to send it to 10 competitors whose brains you want to melt.

Let inception commence.


Fold

As defined on Wikipedia the Fold (higher-order function):

In functional programming, fold, also known variously as reduce, accumulate, compress or inject, is a family of higher-order functions that iterate an arbitrary function over a data structure in some order and build up a return value.

Or more succinctly defined via the F# List.fold signature on MSDN

  • ('State -> 'T -> 'State) -> 'State -> 'T list –> 'State

Fold high-order function implementation examples in F# and C#:

F#
C#

let rec fold f acc = function

    | [] -> acc

    | x :: xs -> fold f (f acc x) xs

public static TAcc Fold<TItem,TAcc>

    (Func<TAcc, TItem, TAcc> f,

     TAcc acc,

     IEnumerable<TItem> items)

{

    foreach (var item in items)

        acc = f(acc, item);

    return acc;

}

 

 

Thusly a BDD framework can be thought of simply as a fold that iterates over a document passing the lines or steps to an arbitrary matching function.

Signature:  fold: (‘context –> step –> ‘context) –> step list –> ‘context

With the accumulator value threaded between calls to the matching function representing the context, which can be explicitly scoped to the scenario under test!


Pattern Match

Given the following Feature document “Addition.txt”:

feature

And an immutable calculator type:

type Calculator = { Values : int list } with
    member this.Push n = {Values=n::this.Values}
    member this.Add() = {Values=[List.sum this.Values]}
    member this.Top = List.head this.Values

 

When folding over the lines in each scenario of the feature:

let feature = parse "Addition.txt"
feature.Scenarios
|> Seq.iter (fun scenario ->
    scenario.Steps
    |> List.fold performStep (Calculator())            
    |> ignore
)

 

And the following matching function is defined:

let performStep (calc:Calculator) (step,line:LineSource) =
    match step with
    | Given "I have entered (.*) into the calculator" [Int n] ->
        calc.Push n                        
    | When "I press add" [] -> 
        calc.Add ()
    | Then "the result should be (.*) on the screen" [Int n] ->
        Assert.AreEqual(n,calc.Top)
        calc            
    | _ -> sprintf "Unmatched line %d" line.Number |> invalidOp

 

Then the feature test succeeds!

inception


Parser

Parsing of the feature file is achieved using TickSpec’s parser module. TickSpec is an Open Source BDD framework written in F#. Underneath TickSpec’s parser is also implemented as a fold (well technically actually a scan) over a Gherkin feature document using regular expressions and Active Patterns.

To support the pattern matching function above the following Active Patterns are required:

[<AutoOpen>]
module Patterns =
    open System.Text.RegularExpressions
    let Regex input pattern =
        let r = Regex.Match(input,pattern)
        if r.Success then 
           Some [for i = 1 to r.Groups.Count-1 do yield r.Groups.[i].Value]
        else None
    let (|Given|_|) (pattern:string) (step) =
        match step with
        | GivenStep input -> Regex input pattern        
        | WhenStep _ | ThenStep _ -> None
    let (|When|_|) (pattern:string) (step) =
        match step with
        | WhenStep input -> Regex input pattern        
        | GivenStep _ | ThenStep _ -> None    
    let (|Then|_|) (pattern:string) (step) =
        match step with
        | ThenStep input -> Regex input pattern        
        | GivenStep _ | WhenStep _ -> None
    let (|Int|) s = System.Int32.Parse(s)

 

All the code to TickSpec including the parser and this sample (in Examples/Functional) are available at: http://tickspec.codeplex.com

 


NUnit

Finally the tests scenarios can be easily run within an NUnit test runner using the TestCaseSource attribute to parameterize a test method:

[<TestFixture>]
type AdditionFixture () =
    [<Test>]
    [<TestCaseSource(typeof<ScenarioSource>,"Scenarios")>]
    member this.TestScenario (scenario:ScenarioSource) =
        scenario.Steps |> Seq.fold performStep (Calculator.Create())
        |> ignore  
    member this.Scenarios =       
        let feature = parse "Addition.txt"        
        feature.Scenarios
        |> Seq.filter (fun scenario ->
            scenario.Tags |> Seq.exists ((=) "ignore") |> not
        )

 

Launch the NUnit Test Runner and the test passes:

NUnit


References

BDD with ``TickSpec`` talk

The podcast for “BDD with TickSpec and Concurrency with Agents” from the F#unctional Londoners Meetup is now available, many thanks to Skills Matter: http://skillsmatter.com/podcast/scala/tickspec-bdd-for-c-sharp-f-sharp

Podcast content:

  • a quick look at F# in MonoDevelop
  • BDD with TickSpec with examples in Visual Studio using NUnit, MbUnit & Silverlight
  • Tomas Petricek on Concurrency with Agents including Chat and Twitter samples

This talk coincided with the November 2010 stable release of TickSpec, which supports both .Net 2.0+ and Silverlight 4. Plus examples for all major .Net testing frameworks.

All TickSpec examples presented are on CodePlex: http://tickspec.codeplex.com

Thanks to everyone who made it a really fun evening!

FSharpMeet

Picture taken at the Slaughtered Lamb pub courtesy of Ankur Gurha

Metadata: data about data

Compilers for statically typed languages like C# and F# maintain data about types from programs. This type data is used at design time to provide IntelliSense and during compilation to generate new types and bind to existing types. We can think about this type data in the abstract as data about data, or Metadata. In statically typed languages the typed Metadata is typically imported and generated from typed programs.


Question:

What if a compiler could be augmented with metadata from other sources, say a database or an XML DTD or a DSL?

Answer:

Then we could map to data easily inside our programs without resorting to either late binding or a code generation step!!!


Examples of accessing attributes of an XML element

1: Late-binding with string literals (F#)

let person = XElement.Parse("<Person Name=\"Bob\"/>")
let name = person.Attribute(XName.Get "Name").Value

 

2: Late-binding with dynamic lookup (F#)

// Define dynamic lookup operator
let (?) (el:XElement) (name:string) = el.Attribute(XName.Get name).Value
// Lookup Name attribute
let name = person?Name

 

3: Code generation using XSD.exe (C#)

string xml = "<Person Name=\"Bob\"/>";
var s = new XmlSerializer(typeof(Person));
var person = s.Deserialize(new StringReader(xml)) as Person;
var name = person.Name;

 

4: Hypothetical code binding (F#)

let person = XmlProvider.LoadXml("<Person Name=\"Bob\"/>")
let name = person.Name

 

The first 1st and 2nd methods using late-binding in effect use string-literals so lack type safety. The 3rd method required a code generation step that generated a whopping 62 lines of C# code to map 1 XML element with a single attribute.

The 4th (hypothetical) method provides all the immediacy benefits of a dynamic language like Python with the compile time safety and ease of use of a statically typed language.

PDC 2010: The Future of F#: Data & Services at Your Finger Tips – Don Syme:

Programming today exhibits a voracious appetite for information, and one of the most important trends in languages today is to make access to data and services fluent and seamless. Come and see the latest from the F# team, and learn how we are extending F# to embed the analytical programmer instantly in a world of typed data and services, whether they be web, enterprise, client or local.

From the slides that are now up on the PDC site, it looks like F# 3.0 is about to deliver!