Модульные тесты Automagic для поддержки контрактов метода объектов в Java?

При разработке приложений Java я часто переопределяю методы объекта (обычно equals и hashCode). Я хотел бы каким-то образом систематически проверять, соблюдаю ли я контракт на методы Object для каждого из моих классов. Например, мне нужны тесты, которые утверждают, что для одинаковых объектов хэш-код также одинаков. Я использую тестовую среду JUnit, поэтому желательно какое-то решение JUnit, в котором я могу автоматически генерировать эти тесты, или какой-нибудь тестовый пример, который может каким-то образом посещать все мои классы и обеспечивать соблюдение контракта.

Я использую JDK6 и JUnit 4.4.

Какую версию junit вы используете? И, что немаловажно, в какой версии jdk вы кодируете? Другими словами, доступны ли аннотации (jdk1.5 или более) или нет (jdk1.4)?

VonC 10.10.2008 07:54

Хороший улов! Я отредактировал свой вопрос, чтобы включить эту информацию. :-)

Julie 10.10.2008 07:57

код-вызов принят и закодирован (хотя первая версия может быть улучшена)

VonC 19.10.2008 19:09

может это поможет: stackoverflow.com/questions/125100/…

yegor256 31.10.2012 14:26

Эта великолепная идея! Среда тестирования, основанная на спецификации Быстрая проверка, основана на аналогичных идеях. Может быть, это можно использовать для этого или послужить источником вдохновения?

Lii 24.03.2015 16:02
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
10
5
1 155
8

Ответы 8

Просто несколько первых мыслей по этому вопросу (которые могут объяснить, почему по прошествии часа до сих пор нет ответа !?;)

Когда дело доходит до решения вопроса, кажется, есть две части:

1 / получить все мои классы. Легко, вы даете имя банке, метод инициализации теста Junit:

  • проверьте, находится ли эта банка в пути к классам выполнения JUnit
  • читать и загружать в него все классы
  • запоминает только те, для которых были объявлены и переопределены equals () и hash () (через Reflection)

2 / тестировать каждый объект
... и в этом кроется загвоздка: вам нужно создать экземпляр этих объектов, то есть создать два экземпляра и использовать их для тестов equals ().

Это означает, что если ваши конструкторы являются аргументами, вы должны учитывать,

  • для аргументов примитивных типов (int, boolean, float, ...) или String, любые комбинации предельных значений (для String, "xxx", "", null; fonr int, 0, -x, + x, -Integer .MIN, + Integer.MAX, ... и т. д.)
  • для непримитивных типов создайте экземпляр тех, которые будут переданы конструктору объекта для тестирования (это означает, что вам необходимо рекурсивно учитывать параметры конструктора этого параметра: примитивные типы или нет)

Наконец, не все параметры, автоматически созданные для этого конструктора, будут иметь смысл с функциональной точки зрения, а это означает, что некоторые из этих значений не смогут создать экземпляр из-за Assert:, которое необходимо обнаружить.

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


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

Затем эти значения аннотации будут прочитаны, и созданные из них экземпляры будут объединены для тестирования equals (). Это позволит снизить количество комбинаций.

Боковой узел: общий тестовый пример JUnit, конечно, проверяет, что для каждого equals () to tests существует:

  • некоторые аннотации, как описано выше (если не доступен только конструктор по умолчанию)
  • соответствующий метод hash () также переопределяется (в противном случае, если он вызовет исключение assert и завершится ошибкой в ​​этом классе)

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

Julie 10.10.2008 08:24

Я думаю, что VonC на правильном пути, но я бы даже согласился на что-то менее сложное, например, параметризованный тест, который принимает объект .class (для которого тестируются методы Object), за которым следует переменное количество аргументов конструктора. Затем вам нужно будет использовать отражение, чтобы найти конструктор, который соответствует типам для переданных аргументов, и вызвать конструктор. Этот тест предполагает, что передаваемые в него параметры создадут действительный экземпляр объекта.

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

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

эти параметры должны быть в коде самого тестируемого класса: это решит проблему «регистрации». См. Мой ответ с более подробной информацией об этой возможной реализации.

VonC 10.10.2008 09:49

Примечание: это определенно вопрос типа "код-вызов". Если вы позволите, я добавлю «ответ сообщества» с некоторыми спецификациями: создайте один тестовый пример JUnit, который по умолчанию будет тестировать себя на предмет переопределения методов равенства и хеширования, определенных для себя: короче говоря, автономный тестовое задание.

VonC 10.10.2008 09:51

Мне нравится идея аннотаций. Я не уверен, что такое «ответ сообщества», но это лучший способ узнать, так что дерзайте!

Julie 10.10.2008 18:43

Право на! Вот это и есть дух;) Еще один вызов кода!

VonC 10.10.2008 18:56

код-вызов принят и закодирован (хотя первая версия может быть улучшена)

VonC 19.10.2008 19:10

У этой проблемы нет "простого" решения, если вы не накладываете строгие ограничения на свои классы.

Например, если вы используете несколько конструкторов для данного класса, как вы можете гарантировать, что все ваши параметры хорошо учтены в ваших методах equals / hash? Как насчет значений по умолчанию? Это вещи, которые, к сожалению, нельзя автоматизировать вслепую.

Что касается equals (), вам необходимо определить в классе, переопределив его, правильные значения для тестирования. Эти значения, вы правы, не будут автоматизированы или угаданы. Но проблема может заключаться в самом механизме тестирования. Теперь это можно автоматизировать!

VonC 10.10.2008 12:30

    public static void checkObjectIdentity(Object a1, Object a2, Object b1) {
        assertEquals(a1, a2);
        assertEquals(a2, a1);
        assertNotSame(a1, a2);
        assertEquals(a1.hashCode(), a2.hashCode());
        assertFalse(a1.equals(b1));
        assertFalse(a2.equals(b1));
        assertFalse(b1.equals(a1));
        assertFalse(b1.equals(a2));
    }

Использование:

        checkObjectIdentity(new Integer(3), new Integer(3), new Integer(4));

Не могу придумать ничего лучше. Добавляйте новые вызовы в checkObjectIdentity при обнаружении ошибки.

и кто создает объекты, которые будут переданы в эту функцию в качестве параметров?

Chii 19.06.2009 17:11

Вы делаете. Не думаю, что «интересные» параметры можно придумать автоматически.

alex 19.06.2009 20:32

[сообщение сообщества здесь, кармы нет;)]

Вот вам еще один код-вызов:

Один java-класс, реализующий тестовый пример JUnit, с основным методом, способным запускать JUnit на самом себе!

Этот класс также:

  • переопределить hash () и equals ()
  • определить несколько атрибутов (с примитивными типами)
  • определить конструктор по умолчанию, а также некоторые конструкторы с различными комбинациями параметров
  • определить аннотацию, способную перечислять "интересные" значения для передачи в этот конструктор
  • аннотировать equals () с этими "интересными" значениями

Тестовый метод принимает параметр имени класса (здесь: это будет сам), проверьте, имеет ли класс с таким именем переопределенный метод equals () с аннотациями "интересные значения". Если это так, он построит соответствующие экземпляры (самого себя) на основе аннотаций и проверит equals ()

Это автономный тестовый класс, который определяет механизм, который может быть обобщен на любой класс с аннотированной переопределенной функцией equals ().

Пожалуйста, используйте JDK6 и JUnit4.4

Этот класс нужно скопировать-вставить в соответствующий пакет пустого java-проекта ... и просто запустить;)


Чтобы добавить еще немного мысли, в ответ Николасу (см. Комментарии):

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

Должны ли аннотации, представляющие потенциальные данные тестирования, никогда не быть в самом классе? ... Эй, это может быть отличный вопрос :)

> определить аннотацию, способную перечислять "интересные" значения для передачи в этот конструктор, я не уверен, что понимаю ... Аннотация добавляет логику тестирования внутри класса для тестирования? Если все в порядке, это ... некрасиво.

Nicolas 10.10.2008 19:08

Куда мне обратиться, чтобы найти ответы на этот вызов? Я новичок в теме "сообщества вики" ...

Julie 10.10.2008 19:18

@Julie ответы будут размещены на этой самой странице, так как они будут правильным ответом на ваш вопрос.

VonC 10.10.2008 19:26

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

VonC 10.10.2008 19:29

Может быть, я неправильно понимаю вопрос (и слишком CS), но не похоже, что проблема, которую вы описываете, разрешима в общем случае.

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

Я не уверен, что какая-либо текущая тестовая среда автоматически урежет и абстрагирует для вас возможности.

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

Julie 10.10.2008 20:00

У меня есть первая грубая реализация для равноправного тестирования с помощью конструктора, используя здесь только примитивные параметры. Просто скопируйте и вставьте его в файл test.MyClass.java и запустите.

Предупреждение: 1720 строк кода (0 ошибок в findbugs, 0 в «модифицированном» стиле проверки, цикломатическая сложность ниже 10 для всех функций).

Смотрите весь код по адресу: Автоматическая проверка функции равенства в классах Java с помощью аннотаций

Большое спасибо за первый удар! У меня есть это в моем списке дел, чтобы проверить это - не могу дождаться, чтобы поиграть с ним.

Julie 20.10.2008 21:45

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

VonC 20.10.2008 22:06

Новый ответ на старый вопрос, но в мае 2011 года Гуава (ранее Google Collections) выпустила класс, который удаляет большую часть шаблонов, под названием EqualsTester. Вам по-прежнему нужно создавать свои собственные экземпляры, но он заботится о сравнении каждого объекта с самим собой, с нулевым значением, с каждым объектом в группе равенства, с каждым объектом в любой другой группе равенства и с секретным экземпляром, который не должен совпадать. Он также проверяет, что a.equals(b) подразумевает a.hashCode() == b.hashCode() во всех этих комбинациях.

Пример из Javadoc:

new EqualsTester()
   .addEqualityGroup("hello", "h" + "ello")
   .addEqualityGroup("world", "wor" + "ld")
   .addEqualityGroup(2, 1 + 1)
   .testEquals();

Другие вопросы по теме