16 people like it.
Like the snippet!
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:
34:
35:
|
open NUnit.Framework
type Calculator() =
member __.Add x y = x + y
let calculator = Calculator()
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 calculator : Calculator
Full name: Script.calculator
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