Проверка на полное равенство в тестах JUnit

Я пишу модульные тесты для объектов, которые клонируются, сериализуются и / или записываются в файл XML. Во всех трех случаях я хотел бы убедиться, что полученный объект "такой же", как и исходный. Я прошел через несколько итераций своего подхода, и, обнаружив недостатки во всех из них, мне стало интересно, что сделали другие.

Моя первая идея заключалась в том, чтобы вручную реализовать метод equals во всех классах и использовать assertEquals. Я отказался от этого подхода после того, как решил, что переопределение равенства для выполнения глубокого сравнения изменяемых объектов - это плохо, поскольку вы почти всегда хотите, чтобы коллекции использовали ссылочное равенство для изменяемых объектов, которые они содержат [1].

Тогда я решил, что могу просто переименуйте метод в contentEquals или что-то в этом роде. Однако, подумав больше, я понял, что это не поможет мне найти те регрессии, которые я искал. Если программист добавляет новое (изменяемое) поле и забывает добавить его в метод clone, то он, вероятно, забудет добавить его и в метод contentEquals, и все эти регрессионные тесты я m писать будет бесполезно.

Затем я написал отличную функцию assertContentEquals, которая использует отражение для проверки значения всех (непереходных) членов объекта, при необходимости рекурсивно. Это позволяет избежать проблем с описанным выше методом сравнения вручную, поскольку по умолчанию предполагается, что все поля должны быть сохранены, а программист должен явно объявить поля для пропуска. Однако бывают законные случаи, когда поле действительно не должно быть таким же после клонирования [2]. Я добавляю дополнительный параметр в assertContentEquals, в котором перечислены поля, которые следует игнорировать, но, поскольку этот список объявлен в модульном тесте, в случае рекурсивной проверки он становится ужасно быстро.

Теперь я думаю о том, чтобы вернуться к включению метода contentEquals в каждый тестируемый класс, но на этот раз реализовано с использованием вспомогательной функции, аналогичной описанной выше assertContentsEquals. Таким образом, при рекурсивной работе исключения будут определены в каждом отдельном классе.

Есть комментарии? Как вы подходили к этой проблеме в прошлом?

Отредактировано, чтобы изложить свои мысли:

[1] Я получил рациональное объяснение того, чтобы не переопределять равенства в изменяемых классах, из этой статьи . После того, как вы вставите изменяемый объект в Set / Map, если поле изменится, его хеш изменится, а его корзина - нет, что нарушит работу. Таким образом, варианты заключаются в том, чтобы не переопределять equals / getHash для изменяемых объектов или иметь политику никогда не изменять изменяемый объект после того, как он был помещен в коллекцию.

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

[2] Одним из примеров в нашей кодовой базе является структура графа, где каждому узлу требуется уникальный идентификатор, который будет использоваться для связывания узлов XML при последующей записи в XML. Когда мы клонируем эти объекты, мы хотим, чтобы идентификатор был другим, а все остальное оставалось прежним. Поразмыслив над этим еще раз, кажется, что вопросы «находится ли этот объект уже в этой коллекции» и «являются ли эти объекты одинаковыми», используют принципиально разные концепции равенства в этом контексте. Первый спрашивает об идентичности, и я бы хотел, чтобы идентификатор был включен, если выполняю глубокое сравнение, а второй спрашивает о сходстве, а я не не хочу, чтобы ID был включен. Это заставляет меня больше склоняться против реализации метода equals.

Вы, ребята, согласны с этим решением, или вы думаете, что внедрение equals - лучший способ?

13
задан Robin Green 20 May 2011 в 19:47
поделиться