Как протестировать на равенство диаграмм составного объекта?

Начиная с TypScript 2.0, для импорта типов используются модули @types npm.

# Implementation package (required to run)
$ npm install --save lodash

# Typescript Description
$ npm install --save @types/lodash 

Теперь, когда этот вопрос был дан ответ, я расскажу, как эффективно импортировать lodash

Отказоустойчивый способ импорта всей библиотеки (в main.ts)

import 'lodash';

Это новый бит здесь:

Реализация более легкого lodash с функциями, которые вам нужны

import chain from "lodash/chain";
import value from "lodash/value";
import map from "lodash/map";
import mixin from "lodash/mixin";
import _ from "lodash/wrapperLodash";

источник: https://medium.com/making-internets/why-using-chain-is-a-mistake-9bc1f80d51ba#.kg6azugbd

PS: В приведенной выше статье интересен вопрос о улучшении времени сборки и уменьшении размера приложения

24
задан skaffman 11 September 2009 в 15:27
поделиться

9 ответов

Что вы можете сделать, так это отобразить каждый объект в XML, используя XStream , а затем использовать XMLUnit для выполнения сравнения в XML. Если они различаются, вы получите контекстную информацию (в форме XPath, IIRC), сообщающую вам, чем отличаются объекты.

например, из документа XMLUnit:

Comparing test xml to control xml [different] 
Expected element tag name 'uuid' but was 'localId' - 
comparing <uuid...> at /msg[1]/uuid[1] to <localId...> at /msg[1]/localId[1]

Обратите внимание на XPath, указывающий расположение различных элементы.

Возможно, не быстро, но это может не быть проблемой для модульных тестов.

8
ответ дан 29 November 2019 в 00:10
поделиться

В Atlassian Developer Blog было несколько статей на эту же тему, и как библиотека Hamcrest может сделать отладку такого типа тестовых сбоев очень простой:

В основном, для такого утверждения:

assertThat(lukesFirstLightsaber, is(equalTo(maceWindusLightsaber)));

Hamcrest вернет вам вывод, подобный этому (в котором показаны только разные поля):

Expected: is {singleBladed is true, color is PURPLE, hilt is {...}}  
but: is {color is GREEN}
10
ответ дан matt b 29 November 2019 в 00:10
поделиться

Юнит-тесты должны иметь четко определенную, единственную вещь, которую они тестируют. Это означает, что в конце вы должны иметь четко определенную, одну вещь, которая может отличаться в этих двух объектах. Если есть слишком много вещей, которые могут отличаться, я бы предложил разделить этот тест на несколько меньших тестов.

1
ответ дан Ula Krukar 29 November 2019 в 00:10
поделиться

Код для этой проблемы существует на http://code.google.com/p/deep-equals/

Используйте DeepEquals.deepEquals (a, b) для сравнения двух Java-объекты для семантического равенства. При этом объекты будут сравниваться с использованием любых пользовательских методов equals (), которые они могут иметь (если у них реализован метод equals (), отличный от Object.equals ()). Если нет, то этот метод будет затем рекурсивно сравнивать объекты поле за полем. При обнаружении каждого поля оно будет пытаться использовать производное equals (), если оно существует, в противном случае оно продолжит повторение.

Этот метод будет работать с циклическим графом объектов следующим образом: A-> B-> C-> A. У него есть обнаружение цикла, так что ЛЮБЫЕ два объекта можно сравнивать, и он никогда не войдет в бесконечный цикл.

Используйте DeepEquals.hashCode (obj) для вычисления hashCode () для любого объекта. Как и deepEquals (), он попытается вызвать метод hashCode (), если реализован пользовательский метод hashCode () (ниже Object.hashCode ()), в противном случае он будет вычислять поле hashCode по полю, рекурсивно (Deep). Также, как и deepEquals (), этот метод будет обрабатывать графы объектов с циклами. Например, A-> B-> C-> A. В этом случае hashCode (A) == hashCode (B) == hashCode (C). DeepEquals.deepHashCode () имеет обнаружение цикла и поэтому будет работать на ЛЮБОМ графе объектов.

3
ответ дан John DeRegnaucourt 29 November 2019 в 00:10
поделиться

Мы используем библиотеку под названием junitx для проверки контракта равенства на все наши «общие» объекты: http://www.extreme-java.de/junitx/

Единственный способ проверить различные части вашего метода equals () - это разбить информацию на что-то более гранулярное. Если вы тестируете глубоко вложенное дерево объектов, то, что вы делаете, на самом деле не является модульным тестом. Вам необходимо протестировать контракт equals () для каждого отдельного объекта в графе с помощью отдельного тестового примера для этого типа объекта. Вы можете использовать объекты-заглушки с упрощенной реализацией equals () для полей с типом класса в тестируемом объекте.

НТН

0
ответ дан RMorrisey 29 November 2019 в 00:10
поделиться

Я бы не использовал toString(), потому что, как вы говорите, он обычно более полезен для создания хорошего представления объекта для отображения или регистрации.

Мне кажется, что ваш «модульный» тест не изолирует тестируемый блок. Например, если ваш объектный граф равен A-->B-->C, а вы тестируете A, ваш модульный тест для A не должен беспокоиться о том, что метод equals() в C работает. Ваш модульный тест для C должен убедиться, что он работает.

Итак, я бы проверил следующее в тесте для метода A equals(): - сравнить два объекта А, которые имеют идентичные B объекты в обоих направлениях, например, a1.equals(a2) и a2.equals(a1). - сравнить два A объекта, которые имеют разные B, в обоих направлениях

Делая это таким образом, с помощью утверждения JUnit для каждого сравнения, вы будете знать, где происходит сбой.

Очевидно, что если в вашем классе больше детей, участвующих в определении равенства, вам нужно будет протестировать еще много комбинаций. Однако я пытаюсь понять, что ваш модульный тест не должен заботиться о поведении чего-либо, кроме классов, с которыми он имеет прямой контакт. В моем примере это означает, что вы могли бы предположить, что C.equals() работает правильно.

Одна морщина может быть, если вы сравниваете коллекции. В этом случае я бы использовал утилиту для сравнения коллекций, таких как commons-collection CollectionUtils.isEqualCollection(). Конечно, только для коллекций в вашей проверяемой единице.

0
ответ дан SingleShot 29 November 2019 в 00:10
поделиться

Если вы хотите, чтобы ваши тесты были написаны на языке scala, вы можете использовать matchete . Это набор средств сопоставления, которые можно использовать с JUnit и которые предоставляют, среди прочего, возможность сравнивать графы объектов :

case class Person(name: String, age: Int, address: Address)
case class Address(street: String)

Person("john",12, Address("rue de la paix")) must_== Person("john",12,Address("rue du bourg"))

будет выдавать следующее сообщение об ошибке

org.junit.ComparisonFailure: Person(john,12,Address(street)) is not equal to Person(john,12,Address(different street))
Got      : address.street = 'rue de la paix'
Expected : address.street = 'rue du bourg'

Как вы можете видеть здесь, я использовал классы case, которые распознаются matchete для того, чтобы погрузиться в граф объектов. Это делается с помощью класса типов под названием Diffable. Я не буду обсуждать здесь классы типов, поэтому предположим, что это краеугольный камень этого механизма, который сравнивает 2 экземпляра данного типа. Типы, которые не являются case-классами (так в основном все типы в Java), получают значение по умолчанию Diffable, которое использует equals. Это не очень полезно, если вы не предоставите Diffable для вашего конкретного типа:

// your java object
public class Person {
   public String name;
   public Address address;
}
// you scala test code
implicit val personDiffable : Diffable[Person] = Diffable.forFields(_.name,_.address)

// there you go you can now compare two person exactly the way you did it
// with the case classes

Итак, мы видели, что matchete хорошо работает с базой кода Java. На самом деле я использовал matchete на моей последней работе в большом Java-проекте.

Отказ от ответственности: я автор matchete:)

0
ответ дан Bruno Bieth 29 November 2019 в 00:10
поделиться

В C ++ есть интересные крайние случаи (некоторые из них и в C ). Рассмотрим

T t;

Это может быть определение или объявление, в зависимости от типа T :

typedef void T();
T t; // declaration of function "t"

struct X { 
  T t; // declaration of function "t".
};

typedef int T;
T t; // definition of object "t".

В C ++ при использовании шаблонов есть еще один крайний случай.

template <typename T>
struct X { 
  static int member; // declaration
};

template<typename T>
int X<T>::member; // definition

template<>
int X<bool>::member; // declaration!

Последнее объявление было не определением. Это объявление явной специализации статического члена X . Он сообщает компилятору: «Если доходит до создания экземпляра X :: member , то не надо» Я кэширую эту отображаемую строку (а также значение hashCode). Обычно это закрыто, но если оставить кешированную строку package-private, вы сможете увидеть ее в своих модульных тестах.

Кстати, это, конечно, не всегда то, что я получаю в поставленных системах, - если тестирование производительности покажет, что это метод слишком медленный, я готов заменить его, но это редкий случай. Пока это произошло только один раз в системе, в которой изменяемые объекты быстро менялись и часто сравнивались.

Я делаю это потому, что написание хорошего хэш-кода не является тривиальным делом и требует testing (*), а использование строки в String позволяет избежать тестирования.

(* Учтите, что шаг 3 в рецепте Джоша Блоха по написанию хорошего метода hashCode заключается в его проверке, чтобы убедиться, что "равно" объекты имеют равные значения hashCode, и убедиться, что вы охватили все возможные варианты, само по себе не тривиально. Распределение более тонкое и еще сложнее для хорошего тестирования)

4
ответ дан 29 November 2019 в 00:10
поделиться

Я пошел по тому же пути, что и вы. У меня также были дополнительные проблемы:

  • мы не можем изменять классы (для equals или toString), которые нам не принадлежат (JDK), массивы и т. Д.
  • равенство иногда отличается в разных контекстах

Например, отслеживание равенства сущностей может полагаться на идентификаторы базы данных, когда они доступны (концепция «той же строки»), полагаться на равенство некоторых полей (бизнес-ключ) (для несохраненных объектов). Для утверждения Junit вам может потребоваться равенство всех полей.


Итак, я закончил тем, что создал объекты, которые проходят через граф, выполняя свою работу по ходу работы.

Обычно существует объект суперкласса Crawling :

  • просматривать все свойства объектов; останавливаться на:

    • перечислениях,
    • каркасных классах (если применимо),
    • при выгруженных прокси-серверах или удаленных соединениях, для Сущностей, использующих идентификаторы, для всех полей или исключительно на основе равенства;). Эти эквалайзеры также часто нуждаются в особых случаях (например, для классов, находящихся вне вашего контроля).

    Возвращаясь к вопросу: эти эквалайзеры могут запоминать путь к различным значениям , что было бы очень полезно в вашем случае JUnit, чтобы понять разницу.

    • Для создания Заказчиков . Например, сохранение сущностей должно выполняться в определенном порядке, и эффективность будет диктовать, что сохранение одних и тех же классов вместе даст огромный импульс.
    • Для сбора набора объектов, которые могут быть найдены на различных уровнях графа. В этом случае очень просто выполнить цикл по результату Collector .

    В качестве дополнения я должен сказать, что, за исключением объектов, производительность которых является реальной проблемой, Мой суперкласс сущностей имеет реализацию по умолчанию этих 4 методов, которые полагаются на это знание, например (нули необходимо позаботиться):

    • toString () печатает «myClass (key1 = value1, key2 = value2)»
    • hashCode () равно «value1.hashCode () ^ value2.hashCode ()»
    • equals () is «value1.equals (other.value1) && value2.equals (other.value2)»
    • compareTo () объединяет сравнение класса, значения1 и значения 2.

    Для сущностей, где производительность вызывает беспокойство, я просто переопределяю эти методы, чтобы не использовать отражение. Я могу проверить в регрессионных тестах JUnit, что две реализации ведут себя идентично.

1
ответ дан 29 November 2019 в 00:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: