Тестирование и логирование — азы программирования
07.08.2007 Квиринг АлексейТесты и нормально организованная трассировка — основные принципы создания больших программных систем. Но чаще всего в них не верят: тратят кучу времени на поиск незначительных ошибок и читают километровые тексты из консолей. Такое встречается даже у программистов с опытом работы несколько лет.
Итак, как мы реализовали эти принципы при создании сервера:
Тесты
К счастью, у нас не большая компания, в который планы известны на 10 лет вперед. У нас маленькая динамичная команда — требования к коду и возможностям сервера меняются каждую неделю. И как обычно бывает в данных обстоятельствах — меняешь код в одном месте, а откликается это совершенно неожиданным образом в другом. Так бы было и у нас, но у нас есть тесты.
Тест — небольшой участок кода, который вызывает методы сервера и сравнивает ответы с эталонными значениям. Время исполнения теста в большинстве случаев мало, правда тестов много, и программисты забывают их запускать после изменения. Но, как известно, лень двигатель прогресса, и было принято решение об интеграции тестов в сервер. То есть, при запуске сервера, он сам себя тестирует. Времени это занимает достаточно мало. Но зато мы всегда видим, корректны ли были наши изменения, и не боимся менять код, а это большой плюс. Для тестов мы используем стандартную библиотеку тестирования — JUNIT. Небольшая и надежная — то, что нам надо.
Очень часто я слышу от программистов, что некогда писать тесты, что надо писать код, или нашу программу невозможно протестировать, она супер уникальная и тесты для неё придумать нельзя. По-моему мнению, есть конечно программы, которые сложно тестировать, например GUI (хотя и для них есть системы тестирования). Но в большинстве случае все тестируется. А что касается затрат времени на тесты, они окупаются сокращением времени при отладке. И код писать надо сразу же с учетом его тестирования, тогда и код получается красивым, и тест сам собой пишется.
Логирование
Чаще всего серверные программы — это некоторые черные ящики, без GUI интерфейса. А программисту важно знать, что же происходит внутри. И начинается. Сплошные trace и System.out. В консоле тонны мусора, если учесть, что на сервере уже более 1000 классов. И непонятно, где начинается один trace, а заканчивается другой. А если над сервером работает несколько человек и каждый решает свою задачу?… В таком случае trace одного человека будет мешать другому, и работа превратится в ад.
К счастью, все придумано за нас, есть проект log4j. Достаточно маленький проект, но решает большую задачу — ведет логи. Логи могут записываться куда угодно: текстовые файлы, механизмы логирования операционных систем, XML формат и самый используемый у нас метод — запись в сокет. Важно и то обстоятельство, что это стандарт. И скорее всего, сторонние библиотеки также будут использовать вашу систему логирования. Например, достаточно просто интегрируется JUNIT.
А далее все просто. Ставится программа для просмотра событий chainsaw. Она умеет получать логи с удаленных логеров. А самое главное, она умеет качественно их показывать. Фильтрация по типам, приоритетам, источникам.
Интеграция JUNIT и Log4j
/** * Реализация ResultPrinter для вывода сообщений JUnit в Log4J * * @author Квиринг А.В (arts80) * */ public class Log4jResultPrinter extends ResultPrinter { /** * @param writer */ public Log4jResultPrinter() { super(null); } @Override public void addError(Test test, Throwable t) { Logger logger = Activator.instance.getLogger(test); logger.error(t.getMessage(), t); } @Override public void addFailure(Test test, AssertionFailedError t) { Logger logger = Activator.instance.getLogger(test); logger.error(t.getMessage() + "", t); } @Override public void endTest(Test test) { Logger logger = Activator.instance.getLogger(test); logger.info(test.toString()); } @Override public void startTest(Test test) { } }
