F#:为什么这两个集合不相等?

时间:2019-06-06 20:02:35

标签: c# f# xunit

在使用XUnit编写某些单元之前,我碰到了一些令人惊讶的事情:

let id = Guid.Empty
let contact = {
    Name = {
        FirstName = "Marcel"
        MiddleInitial = None
        LastName = "Patulacci"
    }
    DateOfBith = new DateTime(1850, 12, 25)
    Address = {
        Address1 = "41 av 8 Mai 1945"
        Address2 = None
        City = "Sarcelles"
        State = None
        Zip = "95200"
    }
    PhoneNumber = {
        DialOutCode = 33
        LocalNumber = "766030703"
    }
    Email = "marcel.patulacci@outlook.com"
}

[<Fact>]
let ``Open an account...``() =
    let event = Event.AccountOpened({
        AccountId = id
        Contact = contact
    })

    let a = [event]
    let b = seq { yield event }

    Assert.Equal(a, b)
  

System.NullReferenceException:对象引用未设置为对象的实例。

尤其令人惊奇的是,考虑到Assert使用的重载为:

public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual)

其中指出:

  

使用默认比较器验证两个序列是否相等。

为什么他们被认为与众不同,为什么Assert.Equal提出System.NullReferenceException

[编辑]

  

System.NullReferenceException:未将对象引用设置为对象的实例。      在Domain.Events.AccountOpenedEvent.Equals(Object obj, IEqualityComparer comp)      在Domain.Events.Event.Equals(Object obj, IEqualityComparer comp)

似乎


type PersonalName = {
    FirstName: string;
    MiddleInitial: string option;
    LastName: string;
}

type Address = {
    Address1: string;
    Address2: string option ;
    City: string;
    State: string option;
    Zip: string;
}

type PhoneNumber = {
    DialOutCode : int;
    LocalNumber: string
}

type Contact = {
    Name: PersonalName;
    DateOfBith: DateTime
    Email: string;
    Address: Address;
    PhoneNumber: PhoneNumber
}

type AccountOpenedEvent = {
    AccountId: Guid
    Contact: Contact
}

type Event =
    | AccountOpened of AccountOpenedEvent

原来event的一个字段是null,而不是event本身。

1 个答案:

答案 0 :(得分:2)

问题出在测试/ id上方定义的contact[<Fact>]上:

let id = Guid.Empty
let contact = {
    Name = {
        FirstName = "Marcel"
        MiddleInitial = None
        LastName = "Patulacci"
    }
    DateOfBith = new DateTime(1850, 12, 25)
    Address = {
        Address1 = "41 av 8 Mai 1945"
        Address2 = None
        City = "Sarcelles"
        State = None
        Zip = "95200"
    }
    PhoneNumber = {
        DialOutCode = 33
        LocalNumber = "766030703"
    }
    Email = "marcel.patulacci@outlook.com"
}

[<Fact>]
let ``Open an account...``() =
    let event = Event.AccountOpened({
        AccountId = id
        Contact = contact
    })

    let a = [event]
    let b = seq { yield event }

    Assert.Equal(a, b)

问题是,当独立运行测试时,idcontact未初始化,因此即使event不是null,联系人也是nullidGuid,也就是struct,它仍然具有值)。

由于F#具有结构相等性,因此如果未初始化该字段之一,则足以使一个字段null使得Assert在其实现的某个时刻失败。

有一些解决方案/解决方法:

  1. 直接在单元测试主体中定义这些变量。
  2. 从单元测试主体中产生这些值的定义方法
let getId() = Guid.Empty
let getContact() = {
    Name = {
        FirstName = "Marcel"
        MiddleInitial = None
        LastName = "Patulacci"
    }
    DateOfBith = new DateTime(1850, 12, 25)
    Address = {
        Address1 = "41 av 8 Mai 1945"
        Address2 = None
        City = "Sarcelles"
        State = None
        Zip = "95200"
    }
    PhoneNumber = {
        DialOutCode = 33
        LocalNumber = "766030703"
    }
    Email = "marcel.patulacci@outlook.com"
}

[<Fact>]
let ``Open an account...``() =
    let id = getId()
    let contact = getContact()

    let event = Event.AccountOpened({
        AccountId = id
        Contact = contact
    })

    let a = [event]
    let b = seq { yield event }

    Assert.Equal(a, b)

虽然这些变通办法起作用,但令我惊讶的是,在运行测试/时未考虑在单元测试函数正上方声明的变量,并且这些变量未初始化。

也许值得再问一个问题,为什么会这样。 从某种意义上说,这是令人惊讶的,如果可以定义一个函数并返回与那些变量几乎相同的东西,则意味着let也已正确编译,那么为什么变量不是这种情况?