F #: Почему эти две коллекции не равны? - PullRequest
2 голосов
/ 06 июня 2019

Писал некоторые юниты, используя 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?

[EDIT]

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 Ответ

2 голосов
/ 06 июня 2019

Проблема заключалась в 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)

Дело в том, что при независимом запуске теста id и contact не инициализируются, следовательно, даже если event не был null, контакт был null (id был Guid aka 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 также правильно скомпилирована, так почему же это не так с переменными?

...