В двух словах, контракт hashCode согласно объекту Java object.hashCode ():
- Хэш-код не должен меняться, если что-то, влияющее на equals (), не изменится.
- equals () подразумевает, что хеш-коды ==
Предположим, что интерес в первую очередь связан с неизменяемыми объектами данных - их информация никогда не меняется после создания, поэтому предполагается, что № 1 сохраняется. Остается №2: проблема просто в подтверждении того, что равенство подразумевает хэш-код ==.
Очевидно, что мы не можем протестировать каждый мыслимый объект данных, если этот набор не является тривиально маленьким. Итак, как лучше всего написать модульный тест, который может выявить распространенные случаи?
Поскольку экземпляры этого класса неизменяемы, существуют ограниченные способы создания такого объекта; этот модульный тест должен по возможности охватывать их все. Вне моей головы точки входа - это конструкторы, десериализация и конструкторы подклассов (которые должны быть сведены к проблеме вызова конструктора).
[Я попытаюсь ответить на свой вопрос путем исследования. Информация от других StackOverflowers - долгожданный механизм безопасности для этого процесса.]
[Это может быть применимо к другим языкам объектно-ориентированного программирования, поэтому я добавляю этот тег.]
источник
Ответы:
EqualsVerifier - относительно новый проект с открытым исходным кодом, который отлично справляется с тестированием контракта на равенство. У него нет проблем, которые есть у EqualsTester от GSBase. Я определенно рекомендовал бы это.
источник
Я бы посоветовал подумать, почему и как это может не выполняться, а затем написать несколько модульных тестов, нацеленных на эти ситуации.
Например, допустим, у вас есть собственный
Set
класс. Два набора равны, если они содержат одинаковые элементы, но могут отличаться базовые структуры данных двух равных наборов, если эти элементы хранятся в другом порядке. Например:MySet s1 = new MySet( new String[]{"Hello", "World"} ); MySet s2 = new MySet( new String[]{"World", "Hello"} ); assertEquals(s1, s2); assertTrue( s1.hashCode()==s2.hashCode() );
В этом случае порядок элементов в наборах может повлиять на их хэш, в зависимости от реализованного вами алгоритма хеширования. Я бы написал такой тест, поскольку он проверяет случай, когда я знаю, что какой-то алгоритм хеширования может выдать разные результаты для двух объектов, которые я определил как равные.
Вы должны использовать аналогичный стандарт со своим собственным классом, каким бы он ни был.
источник
Для этого стоит использовать аддоны junit. Ознакомьтесь с классом EqualsHashCodeTestCase http://junit-addons.sourceforge.net/, вы можете расширить его и реализовать createInstance и createNotEqualInstance, это позволит проверить правильность методов equals и hashCode.
источник
Я бы порекомендовал EqualsTester от GSBase. В основном он делает то, что вы хотите. Однако у меня есть две (незначительные) проблемы:
источник
[На момент написания было опубликовано еще три ответа.]
Повторяю, цель моего вопроса - найти стандартные примеры тестов, чтобы подтвердить это
hashCode
иequals
согласиться друг с другом. Мой подход к этому вопросу состоит в том, чтобы представить общие пути, которые используют программисты при написании рассматриваемых классов, а именно неизменяемых данных. Например:equals()
без письмаhashCode()
. Это часто означает, что равенство определялось как равенство полей двух экземпляров.hashCode()
без письмаequals()
. Это может означать, что программист искал более эффективный алгоритм хеширования.В случае №2 мне кажется, что проблема не существует. Никаких дополнительных экземпляров сделано не было
equals()
, поэтому не требуется дополнительных экземпляров с одинаковыми хэш-кодами. В худшем случае алгоритм хеширования может дать более низкую производительность для хэш-карт, что выходит за рамки этого вопроса.В случае №1 стандартный модульный тест влечет за собой создание двух экземпляров одного и того же объекта с одинаковыми данными, передаваемыми конструктору, и проверку одинаковых хэш-кодов. А как насчет ложных срабатываний? Можно выбрать параметры конструктора, которые просто дают одинаковые хэш-коды на все еще ненадежном алгоритме. Модульный тест, который стремится избегать таких параметров, отвечает духу этого вопроса. Ярлык здесь - проверить исходный код
equals()
, хорошенько подумать и написать тест на его основе, но хотя в некоторых случаях это может быть необходимо, также могут быть общие тесты, которые выявляют общие проблемы - и такие тесты также соответствуют духу этого вопроса.Например, если у тестируемого класса (назовите его Data) есть конструктор, который принимает String, и экземпляры, построенные из Strings, которые дают
equals()
экземпляры, которые былиequals()
, тогда хороший тест, вероятно, будет тестировать:new Data("foo")
new Data("foo")
Мы могли бы даже проверить хэш-код
new Data(new String("foo"))
, чтобы заставить String не интернироваться, хотяData.equals()
, на мой взгляд , это с большей вероятностью даст правильный хеш-код, чем правильный результат.Ответ Эли Кортрайта - это пример того, как тщательно обдумывать способ взлома алгоритма хеширования, основанного на знании
equals
спецификации. Хороший пример специальной коллекции, так как пользовательскиеCollection
s действительно появляются время от времени и весьма склонны к сбоям в алгоритме хеширования.источник
Это один из немногих случаев, когда у меня было бы несколько утверждений в тесте. Поскольку вам нужно протестировать метод equals, вы также должны одновременно проверить метод hashCode. Поэтому в каждом из ваших тестовых случаев метода equals также проверьте контракт hashCode.
A one = new A(...); A two = new A(...); assertEquals("These should be equal", one, two); int oneCode = one.hashCode(); assertEquals("HashCodes should be equal", oneCode, two.hashCode()); assertEquals("HashCode should not change", oneCode, one.hashCode());
И, конечно же, еще одно упражнение - проверка хорошего хэш-кода. Честно говоря, я бы не стал выполнять двойную проверку, чтобы убедиться, что hashCode не изменился в одном и том же прогоне, с подобными проблемами лучше справиться, поймав их в обзоре кода и помогая разработчику понять, почему это не лучший способ писать методы hashCode.
источник
Вы также можете использовать что-то вроде http://code.google.com/p/guava-libraries/source/browse/guava-testlib/src/com/google/common/testing/EqualsTester.java для проверки равенства и hashCode.
источник
Если у меня есть класс
Thing
, как и у большинства других, я пишу классThingTest
, содержащий все модульные тесты для этого класса. У каждогоThingTest
есть методpublic static void checkInvariants(final Thing thing) { ... }
и если
Thing
класс переопределяет hashCode и равен ему, у него есть методpublic static void checkInvariants(final Thing thing1, Thing thing2) { ObjectTest.checkInvariants(thing1, thing2); ... invariants that are specific to Thing }
Этот метод отвечает за проверку всех инвариантов, которые предназначены для удержания между любой парой
Thing
объектов.ObjectTest
Метод он делегирует отвечает за проверку всех инвариантов , которые должны удерживать между любой парой объектов. Какequals
иhashCode
методы всех объектов, этот метод проверяет соответствиеhashCode
иequals
согласованность.Затем у меня есть несколько тестовых методов, которые создают пары
Thing
объектов и передают их парномуcheckInvariants
методу. Я использую разделение по эквивалентности, чтобы решить, какие пары стоит тестировать. Я обычно создаю каждую пару так, чтобы она отличалась только одним атрибутом, плюс тест, который проверяет два эквивалентных объекта.У меня также иногда есть
checkInvariants
метод с тремя аргументами , хотя я считаю, что он менее полезен при обнаружении дефектов findinf, поэтому я делаю это не частоисточник