Verifying the display of a set of objects

I'm looking for a clean set of Test Specific Equality control methods in F # unit tests . 90% of the time, standard structural equality corresponds to the bill, and I can use its unquote to express the connection between mine resultand my expected.

TL DR "I cannot find a clean way to have my own Equality function for one or two properties in a value that 90% of them are well served by structural equality, does F # have a way to map an arbitrary record to a regular equality for only one or two fields?"


An example of a common technique that works for me

When checking a function that maps a 1: 1 data type to a different data type, I often extract the appropriate tuples from both sides in some cases and compare the input and output sets. For example, I have a statement: -

let (====) x y = (x |> Set.ofSeq) = (y |> Set.ofSeq)

So, I can do:

let inputs = ["KeyA",DateTime.Today; "KeyB",DateTime.Today.AddDays(1); "KeyC",DateTime.Today.AddDays(2)]

let trivialFun (a:string,b) = a.ToLower(),b
let expected = inputs |> Seq.map trivialFun

let result = inputs |> MyMagicMapper

test <@ expected ==== actual @>

This allows me Assertso that each of my inputs is displayed on the output without extra outputs.

Problem

The problem is when I want to have my own comparison for one or two fields.

For example, if my DateTime goes through a layer with a low degree of serialization using SUT, I need a test tolerance comparison DateTime. Or maybe I want to do case-insensitive validation for a fieldstring

, Mark Seemann SemanticComparison library Likeness<Source,Destination>, Test Specific, :

  • tuples: F # .ItemX Tuple, .With Expression<T>
  • : TTBOMK sealed F # , SemanticComparison Object.Equals

, , - , .

, , ( , , IEqualityComparer, ?)

- (.. F # Set ). :

let sut (a:string,b:DateTime) = a.ToLower(),b + TimeSpan.FromTicks(1L)

let inputs = ["KeyA",DateTime.Today; "KeyB",DateTime.Today.AddDays(1.0); "KeyC",DateTime.Today.AddDays(2.0)]

let toResemblance (a,b) = TODO generate Resemblance which will case insensitively compare fst and tolerantly compare snd
let expected = inputs |> List.map toResemblance

let result = inputs |> List.map sut

test <@ expected = result @>
+4
1

-, . SemanticComparer<'T>, . . Fil - @ptrelford lib (FSharpValue )!

. , , - , .

-, . ====: -

let (====) x y = (x |> Set.ofSeq) = (y |> Set.ofSeq)

, , - . , , F #, , : comparison ( : equality).

HashSet<T> IEqualityComparer: -

[<AutoOpen>]
module UnorderedSeqComparisons = 
    let seqSetEquals ec x y = 
        HashSet<_>( x, ec).SetEquals( y)

    let (==|==) x y equals =
        let funEqualityComparer = {
            new IEqualityComparer<_> with
                member this.GetHashCode(obj) = 0 
                member this.Equals(x,y) = 
                    equals x y }
        seqSetEquals funEqualityComparer x y 

equals ==|== 'a -> 'a -> bool, args . , , , , . :

sut.Store( inputs)
let results = sut.Read() 

let expecteds = seq { for x in inputs -> x.Name,x.ValidUntil } 

test <@ expecteds ==|== results 
    <| fun (xN,xD) (yN,yD) -> 
        xF=yF 
        && xD |> equalsWithinASecond <| yD @>

SemanticComparer<'T> , , . SemanticComparer<'T>, :

test <@ expecteds ==~== results 
    <| [ funNamedMemberComparer "Item2" equalsWithinASecond ] @>

:

[<AutoOpen>]
module MemberComparerHelpers = 
    let funNamedMemberComparer<'T> name equals = {                
        new IMemberComparer with 
            member this.IsSatisfiedBy(request: PropertyInfo) = 
                request.PropertyType = typedefof<'T> 
                && request.Name = name
            member this.IsSatisfiedBy(request: FieldInfo) = 
                request.FieldType = typedefof<'T> 
                && request.Name = name
            member this.GetHashCode(obj) = 0
            member this.Equals(x, y) = 
                equals (x :?> 'T) (y :?> 'T) }
    let valueObjectMemberComparer() = { 
        new IMemberComparer with 
            member this.IsSatisfiedBy(request: PropertyInfo) = true
            member this.IsSatisfiedBy(request: FieldInfo) = true
            member this.GetHashCode(obj) = hash obj
            member this.Equals(x, y) = 
                x.Equals( y) }
    let (==~==) x y mcs = 
        let ec = SemanticComparer<'T>( seq { 
            yield valueObjectMemberComparer()
            yield! mcs } )
        seqSetEquals ec x y

, !

==|== ( Likeness<'T> ). : -

sut.Save( inputs)

let expected = inputs |> Seq.map (fun x -> Mapped( base + x.ttl, x.Name))

let likeExpected x = expected ==|== x <| (fun x y -> x.Name = y.Name && x.ValidUntil = y.ValidUntil)

verify <@ repo.Store( is( likeExpected)) @> once
+2

All Articles