Одна вещь об автоматизированном тестировании состоит в том, что он требует написания кода для тестирования. Само по себе это не плохо (на самом деле это хорошо, потому что это мешает многим практикам, которых, как правило, следует избегать), но если вы пытаетесь применить модульное тестирование к существующему коду, скорее всего, это не так. был написан в тестируемом виде.
Такие вещи, как синглтоны, статические методы, реестры, локаторы сервисов и т. Д., Вводят зависимости, которые очень трудно смоделировать. Нарушения закона Деметры означают, что слишком много частей вашей кодовой базы слишком много знают о том, как функционируют другие части вашей кодовой базы, создавая дополнительные скрытые зависимости, которые может быть трудно сломать. Все это затрудняет изоляцию модуля от остальной части кода, и если вы не можете тестировать свои модули изолированно, то модульные тесты теряют большую часть своей ценности. Если тест не пройден, это из-за ошибки в тестируемом модуле, или из-за ошибки в одной из его зависимостей, или, возможно, это из-за того, что данные, которые извлекаются через зависимый источник данных, не соответствуют ожиданиям автора теста ? Если вы можете'
Большинство кодовых баз, которые я видел, которые не были созданы с учетом модульного тестирования, по своей сути непроверяемы, так как кодеры, как правило, сосредотачиваются на том, чтобы заставить код работать так, как они ожидают, вместо того, чтобы выполнять работу, необходимую для сохранения свободной связи и явных зависимостей , Код, который был написан с учетом модульного тестирования, имеет тенденцию выглядеть совсем иначе.
Многие люди применяют наивный подход к модульному тестированию, когда начинают его впервые, они думают, что могут просто написать множество тестов для существующей кодовой базы, и все будет хорошо, но это никогда не сработает, потому что вышеупомянутые проблемы. Они начинают обнаруживать, что им нужно чрезмерное количество настроек в модульных тестах, чтобы заставить их работать вообще, и результаты часто сомнительны, потому что отсутствие изоляции в коде означает, что вы не можете отследить, что вызвало сбой теста. Они также имеют тенденцию начинать писать «умные» тесты, которые демонстрируют какой-то очень абстрактный аспект того, как должна работать система. Это может привести к сбою, потому что «умный» модульный тест сам по себе является потенциальным источником ошибок. Тест не прошел из-за ошибки в тестируемом модуле, или из-за ошибки в тесте? Тест должен быть настолько мучительным и простым, что, очевидно, нет возможности скрыть в нем ошибку. На самом деле, лучшие тесты редко бывают длиннее двух строк: первая строка инструктирует тестируемое устройство что-то делать, вторая утверждает, что то, что он сделал, было тем, что ожидалось.
Если ваша команда серьезно относится к внедрению модульного тестирования, было бы неразумно начинать с существующего проекта. Существующие проекты вашей команды, вероятно, невозможно протестировать без серьезного рефакторинга. Вам лучше использовать новый проект в качестве основы для изучения модульного тестирования, поскольку у вас есть чистый лист для работы. Вы можете спроектировать новую кодовую базу для поддержки внедрения зависимостей над синглетами, реестрами и другими такими скрытыми зависимостями, вы можете написать ее так, чтобы она зависела от интерфейсов, а не от реализаций и так далее. Вы также можете (и должны) писать тесты рядом с тестируемым кодом, поскольку последующее написание тестов приводит к модульным тестам, которые проверяют, что тестируемый модуль выполняет то, что, как вы думаете, он должен делать, а не тесты, которые он выполняет. что спецификации говорят, что это должно сделать.
Как только вы приобретете некоторую уверенность в модульном тестировании, ваша команда, вероятно, начнет осознавать недостатки в существующем коде, которые будут препятствием для модульных тестов. Это когда вы можете начать работу по рефакторингу существующего кода, чтобы сделать его более тестируемым. Не будьте амбициозными и не пытайтесь делать это все сразу, или пытайтесь заменить систему, которая работает на совершенно новую, просто начните с поиска битов кодовой базы, которые можно легко протестировать (те, которые не имеют любые зависимости или где зависимости очевидны) и написать тесты для них. Я знаю, что сказал, что написание теста вместе с кодом предпочтительнее, чем написание тестов после, но даже тест, написанный позже, все еще имеет значение в качестве отправной точки. Напишите тесты так, как будто вы ничего не знаете о том, как работает класс, кроме того, что в его спецификациях сказано, что он должен делать. Когда вы запускаете тесты и получаете ошибки, то либо спецификации, либо реализация ошибочны. Дважды проверьте оба, чтобы определить, что не так, и обновите либо тест, либо код соответственно.
После того, как вы сняли низко висящие фрукты, начинается ваша настоящая работа. Вы должны начать находить скрытые зависимости в вашей кодовой базе и исправлять их, по одной за раз. Не становитесь слишком амбициозными на этом этапе, просто придерживайтесь одного модуля за раз или даже только одной проблемы в одном модуле, пока препятствия для тестирования не будут устранены, и вы не сможете перейти к следующему этапу.
TL: DR: Большинство людей думают, что тестирование легко, и вы можете легко переоборудовать тесты в существующий код. Оба эти предположения неверны. Если вы приступаете к проекту, чтобы провести модульное тестирование в своих проектах с учетом обоих этих фактов, у вас больше шансов на успех.