F# Testing with xUnit

I came across a cool tool yesterday for testing F# programs called xUnit and I thought I should give it a try, so here goes.

The first step:  Get it and install it.

The latest version 1.5 is here: http://xunit.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=28060

Don't worry about any documentation there is none -- you've just got to wing it.  I downloaded the release and unzipped it:

You don't need to do any more that that.

The next thing I did was add a reference to the xunit.dll file:

Now for our first test.  In my last post, I showed how to code a mortgage calculator.  I now want to write a test to prove that it works. 

The first thing I did was I took my present value function (along with my other math functions) and stowed them away in a module called MathMod.

Here is the relevant section of MathMod:

-------------------------------------------------------------

module MathMod

#light
open System
open System.Collections.Generic
open System.IO

let pv (i : float) (n : float) =
     1.0 / Math.Pow( (1.0 + i ), n)

let npv x i n =
    x * (pv i n)

let rec valList aList (ir : float) (n : float) =
    match aList with
    | [] -> []
    | h::t -> npv h ir n :: valList t ir (n+1.0)


let presentValue vals (ir : float) =
    List.fold(+)0.0 <| valList vals ir 0.0

----------------------------------------------------------------------

I next added a new file to my project called "Tests.fs".  I figured I would put all my tests in this file so I could then run them all at once.

Now, the concept behind xUnit is that you

(1) create an .exe or .dll that contains some tests;

(2) compile the .exe or .dll; and then

(3) run an axillary program "Test Runner.exe" that looks in your .exe or .dll for methods that are tagged as tests.  The Test Runner  then runs the tests and notifies you if they have succeeded or failed.  Of course, the Test Runner program is not called that, but rather it is officially named xunit.gui.exe (see above).

So, let's try something simple -- this is what my Test.fs file looks like:

----------------------------------------------------------------------

#light

open System
open System.Collections.Generic
open System.IO
open Xunit
open MathMod


[<Fact>]
let PV_1000() =
     let vals = [-1000.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 1100.0]
     let pv0 = presentValue vals 0.1
     Assert.InRange(pv0,(0.0 - 0.0001),(0.0 + 0.0001))

----------------------------------------------------------------------

Notice, I open Xunit (note the capitalization).  I don't know why xUnit is called Xunit ???

Anyway, a test is structured with a tag  [<Fact>].

You then define a method -- I call it PV_1000() in this example.

Caution:  You must append the open parentheses at the end of the function, otherwise the Test Runner won't identify it as a test.  The program will still compile, it's just that the Test Runner won't find it.  It only took me two friggin' hours to figure this out.

In my test I have a series of numbers whose present value I know should be zero (0.0).  The statement:

Assert.InRange(pv0,(0.0 - 0.0001),(0.0 + 0.0001))

Asserts that the result of the present value calculation (pv0) is in the range of 0 +/- 0.0001.  (I can't use equality here because I am using floating point numbers.)  The Test Runner application will find any method tagged as tag  [<Fact>] and run it.  The Assert statement will be executed and will succeed or fail.

So, let's try it.  I first compile the program in F#.  I then run the Test Runner (xunit.gui.exe).  I then open my assembly:

When I open my assembly, the Test Runner shows the tests it has identified.

To run, the test(s), I just click the run button.

The Test Runner then runs the test and indicates if it has passed or failed:

Here it has passed.

Now here is a test I know will fail:

[<Fact>]
let PV_1000_Fail() =
     let vals = [-1000.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 100.0; 1100.0]
     let pv0 = presentValue vals 0.12
     Assert.InRange(pv0,(0.0-0.0001),(0.0+0.0001))

I have changed the discount rate to 12% which should make the present value less than zero.  I recompile the program and run the Test Runner.

Here's what a failure looks like:

The results screen show the failure and the reason it failed.  Note that the present value is now -113.00 -- this value fails the test.

Cool Huh?

Now, the test I really want to write is a test of my mortgage calculator:

-------------------------------------------------------------

[<Fact>]
let mortgageCalc() =

    let mortgage = 200000.0
    let n = 360
    let i = 0.06

    let ones = List.init n (fun one -> 1.0)

    let pv0 = presentValue ones (i/12.0)

    let levelPayment = mortgage / pv0

    //
    //Now Test
    //
    let payments = List.init n (fun one -> levelPayment)

    let pv2 = presentValue payments (i/12.0)
    Assert.InRange(pv2,(mortgage-0.0001),(mortgage+0.0001))

-------------------------------------------------------------

In this test, I calculate my level payment for a $200,000 mortgage over 360 months at 6.00% interest.  I then calculate create a stream of payments equal to my level payment.  I then discount that stream back to the present.  The result is pv2.  I then Assert that the result pv2 is equal (within a tolerance) of my original mortgage.

I compile and run.

We Pass!

 

Print | posted @ Sunday, July 12, 2009 11:28 AM

Comments on this entry:

Gravatar # re: F# Testing with xUnit
by air max pas cher at 2/2/2012 11:27 PM

La conception, a remplacé la version gravée de cette année a apparemment résolu cette question de manière plus approfondie l', prix nike air max tn requin pas cher noire nike air chaussures max coussin d'air qui doit être entièrement due à la technologie et les progrès de la technologie de fabrication à la fin. J'ai essayé sur Nike Air Max II VC, les faits le prouvent. Bien s?r, acheter nike air max tn requin noire cette chaussure d'être bon, mais pour vraiment devenir un ma?tre d'un rebond ou un grand joueur donc, mais aussi parce que leurs propres efforts. et la couleur du cuir avec le bois et les cages de bambou juste pour être matériel, derniere minute nike air max tn requin noire quenelles soupe que beaucoup, panier en bambou, toujours très grasse, donc Nike Dunk SB Xiaolong huile utilisée sur le cuir Cette couleur, en effet, tout comme une casserole cage de boulettes aussi! nike air max de configuration de rembourrage chaussure est toujours la même paire de style principal de cette année est appelée Air Zoom Huarache 2K5, cette chaussure équipée et Air Zoom Flight 95 'de la même Nike Air Max BW, dégriffé nike air max tn requin noire la société a obtenu un développement rapide. L'extrémité extérieure de la conception, la Nike Air Max UPS utilise non seulement la parole à la fin de la tendance commune, mais la première partie de l'ajout d'une tranchée profonde de palmiers rares. Nous avons vu plus haut dans le 2K5 un design similaire, sncf nike air max tn requin noire mais emprise 2K5 est vraiment très bonne. Mais dans la version spéciale de l'onduleur nike air max sur cette conception n'est pas seulement une semelle extérieure à chevrons ordinaires motif. Sole à l'avant-pied sur un sol inégal pour améliorer athlétiques adhérence des chaussures, tandis que le pied joue un r?le protecteur. Suède et nylon avec des matériaux mixtes, air max tn 2008 blanche plus léger que les chaussures traditionnelles, flexibilité, plus élevé, et peut s'adapter à une variété de climat et l'environnement. éléments de conception sont nombreux à travailler ensemble pour créer une chaussure de course classiques. il crée un classique, ainsi que du monde des millions de sentiments du porteur, vous pouvez vraiment sentir et comprendre l'engagement de Nike à la performance du produit, air max tn junior blanche rappelons-nous qu'il est venu de 25 ans de processus d'innovation. Concepteur Bruce Kilgore est l'air max90 air max série et autres produits du fondateur. Sur la couverture, Thomas Marion Jones placé une photo, pas cher nike air max tn requin blanche mais pas pour les performances de Jones.
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 7 and 4 and type the answer here: