Я хотел бы упомянуть еще одну опцию из тех, что вы показали, а именно поместить юнит-тесты в собственный проект (один юнит-тестовый проект для каждого из проектов / компонентов производственного тестирования). Что отличает эти три различных варианта, так это степень, в которой производственный код и тестовый код разделены.
При сравнении разных подходов я вижу следующие критерии:
Поддержание четкого барьера между рабочим кодом и тестовым кодом: производственные сборки не должны содержать тестовый код. Для некоторых видов программного обеспечения это не просто рекомендация, а строгое требование (например, для программного обеспечения, критически важного для безопасности) - даже если тестовый код в рабочем коде будет мертвым или недоступным. Но четкий барьер также может быть полезен при разработке тестов: разделение помогает сначала сосредоточиться на тестах на общедоступном API. И иногда вы хотите доставить производственный код клиенту, а не код модульного тестирования или наоборот (в зависимости от контракта). Это, очевидно, проще для достижения большего производственного кода и тестового кода разделены.
Взаимная прослеживаемость между рабочим кодом и связанным тестовым кодом: прослеживаемость легко достигается, когда производственный код и тестовый код объединены в одну версию. Типичные коммиты в репозиторий могут затем содержать изменения производственного кода и связанные изменения в тестах. Внедрение производственного кода и тестов в разные проекты было бы недостатком - если только вы не можете каким-то образом гарантировать, что оба связанных проекта имеют одинаковую версию, так что всегда будет ясно, какие версии принадлежат друг другу. Если вам придется убедиться в этом вручную, это может привести к сбою.
Принимая во внимание эти критерии, я бы лично сказал, что для многих ситуаций разработки ваше первое решение является наилучшим компромиссом - учитывая, что языковая и инструментальная среда позволяет удовлетворять критериям приемлемым образом. Но, в конце концов, вам придется судить в соответствии с вашим контекстом развития.
Еще одно примечание, возможно, об интеграционных тестах, даже если это выходит за рамки того, о чем вы просили: интеграционные тесты относятся не только к одному компоненту в одной конкретной версии, но и к конфигурации двух или более компонентов в их конкретных версиях. Поэтому для интеграционных тестов более правдоподобно включать их в собственные проекты. Но, опять же, здесь есть исключения ...