Начиная с 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: В приведенной выше статье интересен вопрос о улучшении времени сборки и уменьшении размера приложения
Что вы можете сделать, так это отобразить каждый объект в 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, указывающий расположение различных элементы.
Возможно, не быстро, но это может не быть проблемой для модульных тестов.
В 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}
Юнит-тесты должны иметь четко определенную, единственную вещь, которую они тестируют. Это означает, что в конце вы должны иметь четко определенную, одну вещь, которая может отличаться в этих двух объектах. Если есть слишком много вещей, которые могут отличаться, я бы предложил разделить этот тест на несколько меньших тестов.
Код для этой проблемы существует на 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 () имеет обнаружение цикла и поэтому будет работать на ЛЮБОМ графе объектов.
Мы используем библиотеку под названием junitx для проверки контракта равенства на все наши «общие» объекты: http://www.extreme-java.de/junitx/
Единственный способ проверить различные части вашего метода equals () - это разбить информацию на что-то более гранулярное. Если вы тестируете глубоко вложенное дерево объектов, то, что вы делаете, на самом деле не является модульным тестом. Вам необходимо протестировать контракт equals () для каждого отдельного объекта в графе с помощью отдельного тестового примера для этого типа объекта. Вы можете использовать объекты-заглушки с упрощенной реализацией equals () для полей с типом класса в тестируемом объекте.
НТН
Я бы не использовал 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()
. Конечно, только для коллекций в вашей проверяемой единице.
Если вы хотите, чтобы ваши тесты были написаны на языке 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:)
В 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
, то не надо» Я кэширую эту отображаемую строку (а также значение hashCode). Обычно это закрыто, но если оставить кешированную строку package-private, вы сможете увидеть ее в своих модульных тестах.
Кстати, это, конечно, не всегда то, что я получаю в поставленных системах, - если тестирование производительности покажет, что это метод слишком медленный, я готов заменить его, но это редкий случай. Пока это произошло только один раз в системе, в которой изменяемые объекты быстро менялись и часто сравнивались.
Я делаю это потому, что написание хорошего хэш-кода не является тривиальным делом и требует testing (*), а использование строки в String позволяет избежать тестирования.
(* Учтите, что шаг 3 в рецепте Джоша Блоха по написанию хорошего метода hashCode заключается в его проверке, чтобы убедиться, что "равно" объекты имеют равные значения hashCode, и убедиться, что вы охватили все возможные варианты, само по себе не тривиально. Распределение более тонкое и еще сложнее для хорошего тестирования)
Я пошел по тому же пути, что и вы. У меня также были дополнительные проблемы:
Например, отслеживание равенства сущностей может полагаться на идентификаторы базы данных, когда они доступны (концепция «той же строки»), полагаться на равенство некоторых полей (бизнес-ключ) (для несохраненных объектов). Для утверждения Junit вам может потребоваться равенство всех полей.
Итак, я закончил тем, что создал объекты, которые проходят через граф, выполняя свою работу по ходу работы.
Обычно существует объект суперкласса Crawling :
просматривать все свойства объектов; останавливаться на:
Возвращаясь к вопросу: эти эквалайзеры могут запоминать путь к различным значениям , что было бы очень полезно в вашем случае JUnit, чтобы понять разницу.
В качестве дополнения я должен сказать, что, за исключением объектов, производительность которых является реальной проблемой, Мой суперкласс сущностей имеет реализацию по умолчанию этих 4 методов, которые полагаются на это знание, например (нули необходимо позаботиться):
Для сущностей, где производительность вызывает беспокойство, я просто переопределяю эти методы, чтобы не использовать отражение. Я могу проверить в регрессионных тестах JUnit, что две реализации ведут себя идентично.