Should.BeAvailableInF#
Since I first heard of it, I've found the Should Assertions library convenient for the driver programs that stand in for more formal unit tests for code fragments that I want to blog. However, such main programs have to be written in C#, because you can't access C# extension methods on generic open types from F# -- which is exactly what the general run of Should extensions are.
So, you could write F# code like
Should.ObjectAssertExtensions.ShouldEqual(outstring, instring) |
but that is pretty ugly. And F# type extensions are defined against a specific class, so won't be any use. Which leaves us with generic functions as the best way to wrap the C# extension methods, but
shouldEqual outstring instring |
isn't much of an improvement.
The approach that brings us closer to the C# feel would be to make these methods infix operators.
In an ideal world, we'd use ⊦ (U+22A6 ASSERTION) as a lead character, but that's not yet available without forking the compiler, so I chose to write the functions in the form |-{op}
, doubling the first character of the {op} for the versions that take custom comparison objects, adding a trailing % for variants taking a message, and using !- as an introducer for unary operations like !-/
, where / alludes to ∅ (U+2205 EMPTY SET) for ShouldBeNull
.
Apart from the object/generic assertion, the other type that needs transformation across the language barrier is the ShouldThrow<T>
extension on Action
, which can be wrapped as a generic function taking a unit -> unit
, which uses that to create a delegate for the C# world.
Putting it all together we have module fhould, a pun on f for F# and for the old-fashioned cursive long 's' as in ſhould:
namespace Tinesware.FSharp | |
open System | |
open System.Collections.Generic | |
open Should | |
module fhould = | |
let inline (|->) x y = | |
ObjectAssertExtensions.ShouldBeGreaterThan(x, y) | |
let inline (|->>) (x:'a) (y:'a) (z:IComparer<'a>) = | |
ObjectAssertExtensions.ShouldBeGreaterThan(x, y, z) | |
let inline (|->=) x y = | |
ObjectAssertExtensions.ShouldBeGreaterThanOrEqualTo(x, y) | |
let inline (|->>=) (x:'a) (y:'a) (z:IComparer<'a>) = | |
ObjectAssertExtensions.ShouldBeGreaterThanOrEqualTo(x, y, z) | |
let inline (|-<>) x (y, z) = | |
ObjectAssertExtensions.ShouldBeInRange(x, y, z) | |
let inline (|-<<>>) (x:'a) (y:'a * 'a) (z:IComparer<'a>) = | |
ObjectAssertExtensions.ShouldBeInRange(x, fst y, snd y, z) | |
let inline (|-<) x y = | |
ObjectAssertExtensions.ShouldBeLessThan(x, y) | |
let inline (|-<<) (x:'a) (y:'a) (z:IComparer<'a>) = | |
ObjectAssertExtensions.ShouldBeLessThan(x, y, z) | |
let inline (|-<=) x y = | |
ObjectAssertExtensions.ShouldBeLessThanOrEqualTo(x, y) | |
let inline (|-<<=) (x:'a) (y:'a) (z:IComparer<'a>) = | |
ObjectAssertExtensions.ShouldBeLessThanOrEqualTo(x, y, z) | |
let inline (!-/) x = | |
ObjectAssertExtensions.ShouldBeNull(x) | |
let inline (|-===) x y = | |
ObjectAssertExtensions.ShouldBeSameAs(x, y) | |
let inline (|-@) x y = | |
ObjectAssertExtensions.ShouldBeType(x, y) | |
let inline (|-<@) x (y:Type) = | |
ObjectAssertExtensions.ShouldImplement(x, y) | |
let inline (|-<@%) x y z = | |
ObjectAssertExtensions.ShouldImplement(x, y, z) | |
let inline (|-=) x y = | |
ObjectAssertExtensions.ShouldEqual(x, y) | |
let inline (|-=%) x y (s:String)= | |
ObjectAssertExtensions.ShouldEqual(x, y, s) | |
let inline (|-==) (x:'a) (y:'a) (z:IEqualityComparer<'a>)= | |
ObjectAssertExtensions.ShouldEqual(x, y, z) | |
let inline (|-><) x (y, z) = | |
ObjectAssertExtensions.ShouldNotBeInRange(x, y, z) | |
let inline (|->><<) (x:'a) (y:'a * 'a) (z:IComparer<'a>) = | |
ObjectAssertExtensions.ShouldNotBeInRange(x, fst y, snd y, z) | |
let inline (!-?) x = | |
ObjectAssertExtensions.ShouldNotBeNull(x) |> ignore | |
let inline (|-?%) x y = | |
ObjectAssertExtensions.ShouldNotBeNull(x, y) |> ignore | |
let inline (|-!==) x y = | |
ObjectAssertExtensions.ShouldNotBeSameAs(x, y) | |
let inline (|-!@) x y = | |
ObjectAssertExtensions.ShouldNotBeType(x, y) | |
let inline (|-!=) x y= | |
ObjectAssertExtensions.ShouldNotEqual(x, y) | |
let inline (|-!!=) (x:'a) (y:'a) (z:IEqualityComparer<'a>)= | |
ObjectAssertExtensions.ShouldNotEqual(x, y, z) | |
let shouldThrow<'a when 'a :> Exception> f = | |
ActionAssertionExtensions.ShouldThrow<'a>(new Should.Core.Assertions.Assert.ThrowsDelegate(f)) |
Note that there is no order checking in the range tuples (they are just passed as (low, high) to the corresponding arguments of the underlying code); and that 3-ary methods (custom comparers or message strings) have to be invoked in one or other of these styles to get the association correct
one |->> 0 <| Comparer<int>.Default | |
(one |->> 0) Comparer<int>.Default |
and F# syntax doesn't permit us to define unary/generic ShouldBeType or ShouldImplement operators in the style of (!-@)<'a>
.
No comments :
Post a Comment