15 people like it.

BDD without a framework

A Simple port of the Calculator BDD sample from http://cukes.info. Most BDD frameworks require attributes and shared state to run a test (I'm looking at you specflow!) As the test suite grows, the accidental complexity of different steps initialising (or not) class state becomes a huge problem, resulting in fragile tests. By accumulating state until the assertion, the tests become strongly typed and resilient to change. F#'s backtick methods combined with continuations and lightweight syntax can be taken to extremes to produce a lightweight internal DSL, with efficient results.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
open NUnit.Framework

type Calculator() = 
    member __.Add x y = x + y

let ``Given I have entered`` firstNumber continuation = 
    let calculator = Calculator()
    continuation (firstNumber, calculator)

let ``into the calculator`` (state,calculator) continuation = 
    continuation (state, calculator)

let ``And I have entered`` (firstNumber, calculator) secondNumber continuation = 
    continuation ((firstNumber, secondNumber), calculator)

let ``When I press add`` ((firstNumber, secondNumber), calculator:Calculator) continuation = 
    let actual = calculator.Add firstNumber secondNumber
    continuation actual

let ``Then the result should be`` actual (expected:int) continuation = 
    Assert.AreEqual(expected, actual)
    continuation

let ``on the screen`` = ()
   

[<Test>]
let testAdd ()= 
    ``Given I have entered`` 50 ``into the calculator`` 
     ``And I have entered`` 70 ``into the calculator`` 
     ``When I press add`` 
     ``Then the result should be`` 120 ``on the screen``
 
namespace NUnit
namespace NUnit.Framework
Multiple items
type Calculator =
  new : unit -> Calculator
  member Add : x:int -> y:int -> int

Full name: Script.Calculator

--------------------
new : unit -> Calculator
member Calculator.Add : x:int -> y:int -> int

Full name: Script.Calculator.Add
val x : int
val y : int
val ( Given I have entered ) : firstNumber:'a -> continuation:('a * Calculator -> 'b) -> 'b

Full name: Script.( Given I have entered )
val firstNumber : 'a
val continuation : ('a * Calculator -> 'b)
val calculator : Calculator
val ( into the calculator ) : state:'a * calculator:'b -> continuation:('a * 'b -> 'c) -> 'c

Full name: Script.( into the calculator )
val state : 'a
val calculator : 'b
val continuation : ('a * 'b -> 'c)
val ( And I have entered ) : firstNumber:'a * calculator:'b -> secondNumber:'c -> continuation:(('a * 'c) * 'b -> 'd) -> 'd

Full name: Script.( And I have entered )
val secondNumber : 'c
val continuation : (('a * 'c) * 'b -> 'd)
val ( When I press add ) : (int * int) * calculator:Calculator -> continuation:(int -> 'a) -> 'a

Full name: Script.( When I press add )
val firstNumber : int
val secondNumber : int
val continuation : (int -> 'a)
val actual : int
member Calculator.Add : x:int -> y:int -> int
val ( Then the result should be ) : actual:int -> expected:int -> continuation:'a -> 'a

Full name: Script.( Then the result should be )
val expected : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val continuation : 'a
type Assert =
  static member AreEqual : expected:int * actual:int -> unit + 23 overloads
  static member AreNotEqual : expected:int * actual:int -> unit + 23 overloads
  static member AreNotSame : expected:obj * actual:obj -> unit + 2 overloads
  static member AreSame : expected:obj * actual:obj -> unit + 2 overloads
  static member ByVal : actual:obj * expression:IResolveConstraint -> unit + 2 overloads
  static member Catch : code:TestDelegate -> Exception + 8 overloads
  static member Contains : expected:obj * actual:ICollection -> unit + 2 overloads
  static member Counter : int
  static member DoesNotThrow : code:TestDelegate -> unit + 2 overloads
  static member Equals : a:obj * b:obj -> bool
  ...

Full name: NUnit.Framework.Assert
Assert.AreEqual(expected: obj, actual: obj) : unit
   (+0 other overloads)
Assert.AreEqual(expected: decimal, actual: decimal) : unit
   (+0 other overloads)
Assert.AreEqual(expected: uint64, actual: uint64) : unit
   (+0 other overloads)
Assert.AreEqual(expected: uint32, actual: uint32) : unit
   (+0 other overloads)
Assert.AreEqual(expected: int64, actual: int64) : unit
   (+0 other overloads)
Assert.AreEqual(expected: int, actual: int) : unit
   (+0 other overloads)
Assert.AreEqual(expected: obj, actual: obj, message: string) : unit
   (+0 other overloads)
Assert.AreEqual(expected: float, actual: System.Nullable<float>, delta: float) : unit
   (+0 other overloads)
Assert.AreEqual(expected: float, actual: float, delta: float) : unit
   (+0 other overloads)
Assert.AreEqual(expected: decimal, actual: decimal, message: string) : unit
   (+0 other overloads)
val ( on the screen ) : unit

Full name: Script.( on the screen )
Multiple items
type TestAttribute =
  inherit Attribute
  new : unit -> TestAttribute
  member Description : string with get, set

Full name: NUnit.Framework.TestAttribute

--------------------
TestAttribute() : unit
val testAdd : unit -> unit

Full name: Script.testAdd

More information

Link:http://fssnip.net/pr
Posted:6 years ago
Author:Neil Danson
Tags: bdd