Filed under: F#,NaturalSpec — Steffen Forkmann at 16:46 Uhr
I wrote a lot about NaturalSpec in my last articles. This time I will show how we can use parameterized scenarios.
1. Using predefined scenarios
By writing predefined parameterized scenarios we can easily create a scenario suite with lots of different test cases:
// predefined scenario
let factorialScenario x result =
Given x
|> When calculating factorial
|> It should equal result
[<Scenario>]
let When_calculation_factorial_of_1() =
factorialScenario 1 1
|> Verify
[<Scenario>]
let When_calculation_factorial_of_10() =
factorialScenario 10 3628800
|> Verify
If we run these scenarios with NUnit we will get the following output:
Scenario: When calculation factorial of 1
– Given 1
– When calculating factorial
=> It should equal 1
==> OK
==> Time: 0.0093s
Scenario: When calculation factorial of 10
– Given 10
– When calculating factorial
=> It should equal 3628800
==> OK
==> Time: 0.0018s
2. Using the ScenarioTemplate attribute
The second and shorter option is to use the ScenarioTemplate attribute, which is inherited from NUnit’s new TestCase attribute:
// with ScenarioTemplate Attribute
[<ScenarioTemplate(1, 1)>]
[<ScenarioTemplate(2, 2)>]
[<ScenarioTemplate(5, 120)>]
[<ScenarioTemplate(10, 3628800)>]
let When_calculating_fac_(x,result) =
Given x
|> When calculating factorial
|> It should equal result
|> Verify
This code will create 4 different scenarios, which NUnit will display and report separately:
3. Using ScenarioSource
The third option is to use the ScenarioSource attribute. Here we define a function which generates TestData:
/// with a scenario source
let MyTestCases =
TestWith 12 3 4
|> And 12 4 3
|> And 12 6 2
|> And 1200 40 30
|> And 0 0 0 |> ShouldFailWith (typeof<System.DivideByZeroException>)
And then we have to tell NaturalSpec which TestData a scenario should use:
[<Scenario>]
[<ScenarioSource "MyTestCases">]
let When_dividing a b result =
Given a
|> When dividing_by b
|> It should equal result
|> Verify
Summary
Sometime it makes sense to test a scenario with a bunch of different parameters. We can use the ScenarioTemplate attribute to easily parameterize our scenarios. If we want more flexibility we can use predefined scenarios with custom parameters or the ScenarioSource attribute.
In our sample we want to mock the Dealer.SellCar() functionality. The first step is to create an C#-Interface for the Dealer:
namespace CarSellingLib
{
public interface IDealer
{
Car SellCar(int amount);
}
}
NaturalSpec is using Rhino.Mocks as the underlying mocking framework, so we have to create a reference to Rhino.Mocks.dll in our spec library.
Now we can modify our spec to:
// 1. open module
module CarSpec
// 2. open NaturalSpec-Namespace
open NaturalSpec
// 3. open project namespace
open CarSellingLib
// define reusable values
let DreamCar = new Car(CarType.BMW, 200)
let LameCar = new Car(CarType.Fiat, 45)
// 4. define a mock object and give it a name
let Bert = mock<IDealer> "Bert"
// 5. create a method in BDD-style
let selling_a_car_for amount (dealer:IDealer) =
printMethod amount
dealer.SellCar amount
// 6. create a scenario
[<Scenario>]
let When_selling_a_car_for_30000_it_should_equal_the_DreamCar_mocked() =
As Bert
|> Mock Bert.SellCar 30000 DreamCar // 7. register mocked call
|> When selling_a_car_for 30000
|> It should equal DreamCar
|> It shouldn't equal LameCar
|> Verify
As you can see we changed part 4 (in order to get a mocked IDealer instead of the concrete Dealer). In part 7 we register our mocked behavior. We want that whenever Bert.SellCar is called with parameter 30000 the DreamCar should be returned.
The Verify-function checks if the mocked function has been called. If not the scenario will fail.
If we verify our spec with a NUnit runner we get the following output:
Scenario: When selling a car for 30000 it should equal the DreamCar mocked
Like the TDD principle “Write the tests first” we should write our spec first and use the “Red-Green-Refactor” method.
"Red" – Create a spec scenario that fails
At first I created a F# class library project called “Spec.CarSelling” and added project references to NaturalSpec.dll and nunit.framework.dll (see “Getting started” for further explanations).
Now I can write my first scenario:
// 1. define the module
module CarSpec
// 2. open the NaturalSpec namespace open NaturalSpec
// 3. open project namespace
open CarSellingLib
// 4. define a test context
let Bert = new Dealer("Bert")
// 5. create a method in BDD-style
let selling_a_car_for amount (dealer:Dealer) =
printMethod amount
dealer.SellCar amount
// 6. create a scenario
[<Scenario>]
let When_selling_a_car_for_30000_it_should_equal_my_DreamCar() =
As Bert
|> When selling_a_car_for 30000
|> It should equal (new Car(CarType.BMW, 200))
|> Verify
At this stage the scenario is ready but doesn’t compile. This means we are ready with the "Red"-stage.
"Green" – Make the test the pass
In order to get the test green we have to create a C# class library called CarSellingLib and define the enum CarType and the classes Dealer and Car. Sticking to the YAGNI-principle we implement only the minimum to get the spec green (and ToString()-members for the output functionality).
namespace CarSellingLib
{
public enum CarType
{
BMW
}
}
namespace CarSellingLib
{
public class Car
{
public Car(CarType type, int horsePower)
{
Type = type;
HorsePower = horsePower;
}
public CarType Type { get; set; }
public int HorsePower { get; set; }
public override string ToString()
{
return string.Format("{0} ({1} HP)", Type, HorsePower);
}
public override bool Equals(object obj)
{
var y = obj as Car;
if(y == null) return false;
return Type == y.Type && HorsePower == y.HorsePower;
}
}
}
using System;
namespace CarSellingLib
{
public class Dealer
{
public Dealer(string name)
{
Name = name;
}
public string Name { get; set; }
public Car SellCar(int amount)
{
return new Car(CarType.BMW, 200);
}
public override string ToString()
{
return Name;
}
}
}
When we add a project reference to our spec-project the UnitTests should pass and we have completed the "Green" step. (See "Getting started" if you don’t know how to run the spec.) Now we can add some more scenarios to our spec:
// 1. define the module module CarSpec
// 2. open NaturalSpec-Namespace
open NaturalSpec
// 3. open project namespace
open CarSellingLib
// 4. define a test context
let Bert = new Dealer("Bert")
// define reusable values
let DreamCar = new Car(CarType.BMW, 200)
let LameCar = new Car(CarType.Fiat, 45)
// 5. create a method in BDD-style
let selling_a_car_for amount (dealer:Dealer) =
printMethod amount
dealer.SellCar amount
// 6. create a scenario
[<Scenario>]
let When_selling_a_car_for_30000_it_should_equal_the_DreamCar() =
As Bert
|> When selling_a_car_for 30000
|> It should equal DreamCar
|> It shouldn't equal LameCar
|> Verify
[<Scenario>]
let When_selling_a_car_for_19000_it_should_equal_the_LameCar() =
As Bert
|> When selling_a_car_for 19000
|> It should equal LameCar
|> It shouldn't equal DreamCar
|> Verify
// create a scenario that expects an error
[<Scenario>]
[<Fails_with "Need more money">]
let When_selling_a_car_for_1000_it_should_fail_with_Need_More_Money() =
As Bert
|> When selling_a_car_for 1000
|> Verify
Now we are in the “Red”-Phase again.
"Refactor" – rearrange your code to eliminate duplication and follow patterns
After making the spec "Green" and doing some refactoring the project code could look like this:
namespace CarSellingLib
{
public enum CarType
{
Fiat,
BMW
}
}
namespace CarSellingLib
{
public class Car
{
public Car(CarType type, int horsePower)
{
Type = type;
HorsePower = horsePower;
}
public CarType Type { get; set; }
public int HorsePower { get; set; }
# region ToString, Equals
public override string ToString()
{
return string.Format("{0} ({1} HP)", Type, HorsePower);
}
public override bool Equals(object obj)
{
var y = obj as Car;
if(y == null) return false;
return Type == y.Type && HorsePower == y.HorsePower;
}
#endregion
}
}
using System;
namespace CarSellingLib
{
public class Dealer
{
public Dealer(string name)
{
Name = name;
}
public string Name { get; set; }
public Car SellCar(int amount)
{
if (amount > 20000)
return new Car(CarType.BMW, 200);
if (amount > 3000)
return new Car(CarType.Fiat, 45);
throw new Exception("Need more money");
}
public override string ToString()
{
return Name;
}
}
}
The spec output should look like the following:
Scenario: When selling a car for 1000 it should fail with Need More Money
– Should fail…
– As Bert
– When selling a car for 1000
Scenario: When selling a car for 19000 it should equal the LameCar
– As Bert
– When selling a car for 19000
=> It should equal Fiat (45 HP)
=> It should not equal BMW (200 HP)
==> OK
Scenario: When selling a car for 30000 it should equal my DreamCar
– As Bert
– When selling a car for 30000
=> It should equal BMW (200 HP)
==> OK
Scenario: When selling a car for 30000 it should equal the DreamCar
– As Bert
– When selling a car for 30000
=> It should equal BMW (200 HP)
=> It should not equal Fiat (45 HP)
>==> OK
Test-Driven development (TDD) is a well known software development technique and follows the mantra “Red-Green-Refactor”. Behavior-Driven Development (BDD) is a response to TDD and introduces the idea of using natural language to express the Unit Test scenarios.
There are a lot of popular testing frameworks around which can be used for BDD including xUnit.net ,NUnit, StoryQ, MSpec, NSpec and NBehave. Most of them can be used with fluent interfaces and therefore provides a good readability of the sources. Some of them even provide the possibility to generate a spec in natural language out of passed Unit tests.
What is a spec?
“A specification is an explicit set of requirements to be satisfied by a material, product, or service.”
American Society for Testing and Materials (ASTM) definition
A spec is an important document for the communication process – it enables domain experts to communicate with developers. But how can you verify the compliance with the spec? The answer is: you have to write unit tests. Even with the mentioned frameworks there is a lot of work to do in order to translate a spec scenario into a Unit Test.
Question 7 in the famous Joel Test is “Do you have a spec?”.
The idea of NaturalSpec is to give domain experts the possibility to express their scenarios directly in compilable Unit Test scenarios by using a Domain-specific language (DSL) for Unit Tests. NaturalSpec is completely written in F# – but you don’t have to learn F# to use it. You don’t even have to learn programming at all.
Example 1 – Specifying a list
Let’s consider a small example. If we want to test a new List implementation a spec could look like this:
[<Scenario>]
let When_removing_an_3_from_a_small_list_it_should_not_contain_3() =
Given [1;2;3;4;5] // “Arrange” test context
|> When removing 3 // “Act”
|> It shouldn't contain 3 // “Assert”
|> It should contain 4 // another assertion
|> Verify // Verify scenario
I used BDD style here and expressed my scenario in a quite natural language. As the comments are indicating the scenario is following the Arrange Act Assert (“AAA”) pattern.
With the Keyword “Given” I can create a test context (the objects I want to test). In this sample I created a list with 5 elements. With the keyword “When” I call a function which does something with my test context. In this case I want to remove the value 3. In the Assert section (keywords “It should” or “It shouldn’t”) I can give some observations, which should hold for my manipulated test context.
When I run this scenario via a NUnit runner (i am using TestDriven.Net) I get the following output:
Scenario: When removing an 3 from a small list it should not contain 3
– Given [1; 2; 3; 4; 5]
– When removing 3
=> It should not contain 3
=> It should contain 4
==> OK
Example 2 – Specifying a factorial function
If you implement factorial function the spec could look like this:
[<Scenario>]
let When_calculating_fac_5_it_should_equal_120() =
Given 5
|> When calculating factorial
|> It should equal 120
|> Verify
[<Scenario>]
let When_calculating_fac_1_it_should_equal_1() =
Given 1
|> When calculating factorial
|> It should equal 1
|> Verify
[<Scenario>]
let When_calculating_fac_0_it_should_equal_0() =
Given 0
|> When calculating factorial
|> It should equal 1
|> Verify
And the output of NaturalSpec would look like this:
Scenario: When calculating fac 0 it should equal 0
– Given 0
– When calculating factorial
=> It should equal 1
==> OK
Scenario: When calculating fac 1 it should equal 1
– Given 1
– When calculating factorial
=> It should equal 1
==> OK
Scenario: When calculating fac 5 it should equal 120
– Given 5
– When calculating factorial
=> It should equal 120
==> OK
Sometimes we need a function which finds the m smallest elements in a list or array.
We can use the idea of bubble sort to maintain an sorted array of the m smallest elements so far. This idea gives us a O(m*n) algorithm which solves our problem:
let bubble array element =
let rec bubbleStep i =
if i < (array |> Array.length) then
match array.[i] with
| None ->
if i = array.Length - 1 || array.[i+1] <> None then
array.[i] <- Some element
bubbleStep (i+1)
| Some x ->
if element < x then
if i = 0 then
array.[i] <- Some element
else
array.[i-1] <- Some x
array.[i] <- Some element
bubbleStep (i+1)
bubbleStep 0
array
let mMin seq m =
Seq.fold bubble (Array.create m None) seq
We can easily generalize this function to compute maximum and minimum:
let bubble f array element =
let rec bubbleStep i =
if i < (array |> Array.length) then
match array.[i] with
| None ->
if i = array.Length - 1 || array.[i+1] <> None then
array.[i] <- Some element
bubbleStep (i+1)
| Some x ->
if f element xthen
if i = 0 then
array.[i] <- Some element
else
array.[i-1] <- Some x
array.[i] <- Some element
bubbleStep (i+1)
bubbleStep 0
array
let mBubble f seq m =
Seq.fold (bubble f) (Array.create m None) seq
let mMin seq m = mBubble (<) seq m
let mMax seq m = mBubble (>) seq m
If we don’t want to store Option-Values we can use this simplification:
let bubble f m array element =
let rec bubbleStep i =
if i < (array |> Array.length) then
let x = array.[i]
if f element x then
if i = 0 then
array.[i] <- element
else
array.[i-1] <- x
array.[i] <- element
bubbleStep (i+1)
if Array.length array < m then
let newArray = Array.append [|element|] array
newArray
|> Array.sort
(fun a b ->
if a = b then 0
elif f a b then 1 else -1)
newArray
else
bubbleStep 0
array
let mBubble f seq m =
Seq.fold (bubble f m) Array.empty seq
let mMin seq m = mBubble (<) seq m
let mMax seq m = mBubble (>) seq m
“Funktionale Programmiersprachen nehmen seit geraumer Zeit einen hohen Stellenwert in der Wissenschaft ein. Demnächst könnte es eine dieser Sprachen sogar aus dem Forschungsbereich direkt in den Mainstream schaffen. Visual Studio 2010 wird neben C# und VB.NET die funktionale Programmiersprache F# als dritte Hauptsprache anbieten. Der Vortrag soll einen Einblick in funktionale Konzepte und deren Umsetzung in F# geben. Insbesondere soll auf “Funktionen höherer Ordnung”, Typinferenz, Currying, Pattern Matching, “Unveränderlichkeit” und parallele Programmierung eingegangen werden.”
[Abstract]
Warning: Creating default object from empty value in /www/htdocs/w007928a/blog/wp-content/plugins/wp-referencesmap/GoogleMapsWrapper.php on line 55
Filed under: Google — Steffen Forkmann at 19:03 Uhr
Irgendwie bringt Google Maps doch immer wieder neue Kuriositäten. Wenn man sich den Fußweg von Barcelona nach Marseille anzeigen lässt, dann schlägt das Programm eine Fährtour über Genua und Korsika vor und weist auch ausdrücklich darauf hin, dass es dort möglicherweise keine Bürgersteige gibt. 😉