Модульное тестирование, статика и фабрики

#java #unit-testing #junit #static-methods

#java #модульное тестирование #junit #статические методы

Вопрос:

Я реализую модель на Java, которая требует перебора коллекции и прохождения ряда этапов идентификации, это включает циклы for, циклы while и т.д. Это та вещь, которую я хочу протестировать на детальном уровне, чтобы у меня была уверенность, что она была реализована должным образом.

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

В основном мой вопрос сводится к двум противоречивым советам, которые я получил:

1) Статика — это зло. Не трогайте статику. Также не тестируйте рядовых пользователей, вы, вероятно, хотите, чтобы вместо них был класс.
2) Используйте фабрики для создания, чтобы разрешить внедрение зависимостей с использованием параметров — потенциально позволяя использовать mocks и заглушки для изоляции.

В моем примере я хочу выполнить операцию в соответствии с:

 double height = 223.42; // this was set iterating over a collection of doubles
//blah
HeightBounds b = HeightBounds.getHeightBounds(height);
//more blah
  

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

Здравый смысл подсказывает мне, что со статическими фабриками все в порядке и что их легко тестировать, но не упускаю ли я чего-то ослепительно очевидного, учитывая, что я изучаю дизайн, основанный на тестировании?

Спасибо

Комментарии:

1. Вы должны выполнять модульное тестирование каждого метода, который имеет смысл тестировать, включая статические методы.

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

3. проблема в том, что я мог бы выполнять проверку границ внутри класса как вызов статического метода, но это было бы очень процедурно, и я не хочу указывать это в моем общедоступном интерфейсе. Одним из способов, которым я мог бы протестировать статические методы без изменения видимости, было бы расширить класс для тестирования и создать методы для их вызова, но все равно все, что я читаю, люди говорят «статические методы — это запах кода», вы должны использовать классы. Учитывая, что я работаю во встроенной среде, я предпочитаю идею избегать создания объектов в отличие от идеального OO-дизайна… все еще раздумываю!

Ответ №1:

Класс static factory обеспечивает связь между вашим классом и HeightBounds классом. это может затруднить тестирование вашего класса, если, например, HeightBounds он отключится и будет искать информацию в базе данных или считывать данные из веб-службы и т.д. И т.п.

Если бы вы вместо этого внедрили IHeightBounds реализацию в свой класс, то вы могли бы смоделировать это, чтобы вы могли проверить, что происходит, когда зависимости вашего класса выполняют определенные действия.

Например, что, если HeightBounds возникнет исключение? или возвращает null? Или вы хотите проверить, когда возвращается конкретное HeightBound ? С интерфейсом легко имитировать такое поведение, со статической фабрикой это сложнее, поскольку вам приходится обрабатывать данные для создания желаемых результатов в классе.

У вас все еще может быть только одна реализация HeightBounds , и вы сможете протестировать ее изолированно, но вы сможете протестировать свой метод, описанный выше, даже не имея реальной реализации.

Вероятно, у меня был бы IHeightBoundFactory интерфейс и я внедрил бы реализацию в класс.

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

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

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

Комментарии:

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

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

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

4. Я очень хорош в открытых вопросах 🙂 Я довольно часто сталкиваюсь с параличом анализа

5. Этот ответ сильно основан на предположении , что static метод нельзя легко подделать… Немного странно, что люди обычно принимают это как должное, даже если предположение неверно.

Ответ №2:

Статика — это зло. Не трогайте статику.

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

Также не тестируйте рядовых пользователей, вы, вероятно, хотите, чтобы вместо них был класс.

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

Ответ №3:

Проблема со статическими фабриками заключается в том, что вы не можете заменить фабрики (и иногда объекты, созданные фабриками) на mocks. — Это одна из причин, почему контейнеры IOC настолько полезны.

Комментарии:

1. Спасибо, IoC Containers было очень хорошим ключевым словом для Google, с тех пор я читаю это: martinfowler.com/articles/injection.html

Ответ №4:

Статические методы в основном уничтожают модульное тестирование.

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

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

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

У Миско Хевери есть очень хороший пост на эту тему.

Комментарии:

1. Самих DistanceBounds еще не существует — я все еще уточняю, какие объекты необходимы и процесс, имея в виду, как я буду их тестировать. Возможно, TDD — это не то, что я имел в виду, но в основном вместо того, чтобы заранее определять все мои тесты / код в стиле водопада, я собираюсь писать их по мере написания кода, метода за раз … конечно, тесты управляют моим дизайном, но да, возможно, это не TDD или какой-либо формальный процесс.

Ответ №5:

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

Итак, это неправда, что статические методы убивают модульное тестирование.

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

И полное подтверждение для написания тестов в первую очередь — это также поможет вам разработать лучший API, правильно сократить ваши классы.

Комментарии:

1. Причина, по которой люди говорят, что статические методы уничтожают модульное тестирование, заключается не в том, что вы не можете протестировать статические методы, а в том, что код, вызывающий статические методы, трудно тестировать изолированно. В случае Java вам пришлось бы использовать фреймворк, поддерживающий инструментирование байт-кода (например, JMockit), чтобы имитировать / подделать / заглушку / без метода. Насколько я понимаю, это не идеально (хотя я только недавно узнал об этой возможности).

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

3. Я предположил, что ваше утверждение «Итак, неправда, что статические методы убивают модульное тестирование». был ответом на противоположное. Причина, по которой я прокомментировал, заключается в том, что я почувствовал, что в вашем ответе говорится: «статические методы можно использовать, потому что вы также можете их протестировать», и я хотел добавить то, что, как я понимаю, является недостатками: издеваться над ними сложно, потому что это сложнее, а также медленнее, чем обычное издевательство. В общем, вы хотите, чтобы ваши unittests были простыми (чтобы вы могли их поддерживать) и быстрыми (чтобы вы могли запускать их часто).