Вот некоторые возможные практические преимущества размещения тестового кода непосредственно в классе, который он тестирует.
Все следующие пункты - это в основном разные эффекты, проистекающие из того, что хранение тестов с кодом затрудняет их игнорирование. Это напоминает нам, что код и тесты тесно связаны и одинаково важны. Объединение их помогает нам объединить две концепции в одну, достигая типов (классов), которые достигают лучшего концептуального «сжатия» (подумайте, как классы иногда называют «абстракциями», но «сжатие» становится основной причиной, по которой мы не всегда хотим развязка в наших абстракциях).
Это снижает вероятность того, что мы, программисты, работающие над кодом, буквально забудем на время подумать о тестах. Это может выглядеть как изменение кода сначала без единой мысли о каких-либо тестах, затем запуск тестов позже перед выпуском / выталкиванием / слиянием, и видение того, что они все еще проходят, не думая о том, оправданы ли еще тесты для кода, который они написал, учитывая характер изменений.
Программистам труднее быть ленивыми. Если тест «полностью», то программист должен найти тестовый код, прочитать его, найти соответствующую часть кода, который они только что рассматривали / думали об изменении. Но если у вас есть что-то более похожее на метод, который тестирует метод, а затем метод, который он тестирует, вам нужно быть очень ленивым, чтобы не «испытывать желание» найти тест.
Если программисты уже заботятся о качестве кода в реальном коде, этот подход помогает им одинаково заботиться о качестве кода в тестах, потому что между ними меньше различий. Это действительно важно, потому что качество кода в тестах, возможно, даже более важно, чем качество кода остальной части кода, потому что необходимо смехотворно тщательно заботиться о вашем тестовом коде, чтобы в конечном итоге тесты больше не сдерживали команду. чем они им помогают, что на самом деле является самой сложной частью правильного использования тестов.
Если вы выполняете TDD или даже пишете какой-то код, а затем сразу пишете тест, вы часто будете возвращаться назад и вперед между тестом и кодом, пытаясь сравнить их. Но если они принадлежат к одному и тому же классу, это делает процесс сравнения двух более естественным и менее познавательным. Это также способствует тому, что тесты не удерживают вас (эмоционально и энергетически) больше, чем помогают, что может привести к разочарованию в тестах и удалению их всех.
Это приводит к более естественному повторному использованию кода между тестами и кодом. Это служит двойной цели. Это позволяет объединить идею утверждений и тестов во время выполнения в одну вещь, по крайней мере, некоторое время. То, как вы это сделаете, - это использование методов, которые не преследуют никакой цели, кроме выполнения утверждений, которые в случае сбоя выдают исключение. Затем вы можете повторно использовать эти методы утверждений как из кода, так и для проверки контрактов, а также в тестах для проверки кода. Проверка контрактов и создание исключений при их нарушении в любом случае считается хорошей практикой, и это делает ее более естественной. Тогда некоторые из тестов могут даже стать такой простой вещью, когда они просто правильно выполняют правильные методы, а затем методы буквально в некоторой степени проверяют себя, потому что они уже вызывают эти методы утверждений. Затем вы можете решить оставить все эти утверждения при выпуске кода, чтобы обеспечить лучший мониторинг и отчетность по производству, или отключить хотя бы некоторые из них для более быстрого кода. Они могут даже быть включены в производство по мере необходимости, когда требуется более высокий уровень отчетности. Повторное использование также служит для уменьшения общего объема необходимого тестового кода, что еще больше помогает нам поддерживать вес тестов на себе.
Тесты могут быть хорошей формой документации. Сохраняя тесты с кодом, вы сохраняете документацию с кодом, который он документирует, что зачастую является лучшим способом для программистов действительно понять код.
Упрощает рефакторинг в случае, если у вас уже было однозначное соответствие между тестовыми файлами и тестируемым кодом. Если вы переименовываете класс, вам не нужно также переименовывать его тест.
Размещение тестов в коде делает более естественным отображение тестов один на один и кода, который он тестирует. Некоторые люди говорят, что это плохо, потому что тесты должны проверять поведение, а не классы / методы, но когда это возражение действительно, это фактически запах кода. Код должен быть написан так, чтобы это было естественно для языка, на котором он написан, и почти заставляет его выглядеть так, как если бы этот язык был разработан только для вашего кода. Затем код должен объединить эту концепцию с использованием преимуществ языковых конструкций, чтобы код был наиболее естественным выражением / спецификацией фактического конечного поведения, насколько это возможно в этом языке. Если тестирование «кода» означает, что вы не тестируете «поведение», у вас уже была проблема с качеством кода. В типо-ориентированном программировании (подумайте об объектно-ориентированном программировании, которое в большинстве современных ОО-языков на самом деле является класс-ориентированным программированием, а на самом деле это тип-ориентированное программирование, потому что классы - это способ для определения ваших собственных типов), это имело бы смысл, поскольку Вы хотите, чтобы каждый тип был индивидуально протестирован на техническом уровне (точность выполнения операций над значениями этого типа). Это потому, что каждый новый тип, который вы создаете, становится основной частью языка, на котором вы пишете код, и вы не хотите, чтобы ваш язык программирования глючил, верно? Это также не исключает программирования функций конечного пользователя напрямую, потому что они могут быть реализованы как их собственный тип (представьте тип для каждой веб-страницы или тип для каждой команды API. Поэтому некоторые типы будут гораздо ближе к конечному пользователю, и другие будут гораздо более низкого уровня, но оба должны превзойти на уровне типа, так как это то, что они есть.