Подумайте какие сценарии использования вашего модуля могут быть, что поступает на вход и что должно быть на выходе.
Вход и выход это не только параметры конструктора/методов и возвращаемое return значение, но и состояние БД, ФС и т. п., а для выхода ещё и исключения. Для каждого сценария подготавливаете
с нуля вход (создаёте нужные объекты рантайма, файлы, схему и данные в БД), выполняете сценарий (в простейшем случае вызов одного метода) и проверяете выход.
Если архитектура не заточена под тестирование, то, скорее всего, тесты будут большие, хрупкие и медленные, ведь кроме вашей собственной логики они будут проверять логику системы, фреймворка,, библиотек, ОС и т. п. Наступает время рефакторинга с целью облегчения тестирования. Скажем все файловые операции в тестируемом модуле выделяете в методы отдельного класса FileSystem, который передаётся вашему модую в параметрах или контейнере. Убеждаетесь, что первая группа тестов всё ещё работает после рефакторинга, копируете её как новую группу и начинаете заменять в ней реальные вызовы на фэйковые, моки и стабы. Получаете более лёгкие тесты. Как-то так.
Мне в своё время очень помогла книга
Эффективная работа с унаследованным кодом