У меня есть приложение .NET с веб-интерфейсом, интерфейсом WCF-службы Windows. Приложение довольно простое - оно требует некоторого пользовательского ввода, отправляя его в сервис. Служба делает это - принимает данные (электронная таблица Excel), извлекает элементы данных, проверяет базу данных SQL, чтобы убедиться, что элементы еще не существуют - если они не существуют, мы делаем запрос в реальном времени стороннему поставщику данных и получить результаты, вставив их в базу данных. По пути он делает некоторые записи.
У меня есть класс Job с одним общедоступным ctor и публичным методом Run (). Ctor принимает все параметры, а метод Run () выполняет всю вышеуказанную логику. Каждый логический фрагмент функциональности разделен на отдельный класс - IParser выполняет синтаксический анализ файла, IConnection взаимодействует с поставщиком данных, IDataAccess осуществляет доступ к данным и т. Д. Класс Job имеет частные экземпляры этих интерфейсов и использует DI для создания фактические реализации по умолчанию, но позволяют пользователю класса вводить любой интерфейс.
В реальном коде я использую ctor по умолчанию. В моих модульных тестах для метода Run () я использую все фиктивные объекты, созданные с помощью NMock 2.0. Этот метод Run () по сути является функцией «верхнего уровня» этого приложения.
Теперь вот моя проблема / вопрос: модульные тесты для этого метода Run () сумасшедшие. У меня есть три фиктивных объекта, которые я отправляю в ctor, и каждый фиктивный объект устанавливает ожидания для себя. В конце я проверяю. У меня есть несколько различных потоков, которые может принять метод Run, каждый из которых имеет свой собственный тест - он может найти все, что уже есть в базе данных, и не сделать запрос поставщику ... или может быть сгенерировано исключение, и статус задания может быть установленным на «fail» ... ИЛИ у нас может быть случай, когда у нас не было данных, и нам нужно было сделать запрос поставщика (поэтому все эти вызовы функций должны быть выполнены).
Теперь - прежде чем вы кричите на меня и говорите: «Ваш метод Run () слишком сложен!» - этот метод Run - всего лишь 50 строк кода! (Он вызывает некоторые частные функции; но весь класс занимает всего 160 строк). Поскольку вся «настоящая» логика выполняется в интерфейсах, объявленных в этом классе. однако самый большой модульный тест для этой функции - это 80 строк кода с 13 вызовами Expect.BLAH () .. _
Это делает перефакторинг огромной болью. Если я хочу изменить этот метод Run (), я должен отредактировать свои три модульных теста и добавить / удалить / обновить вызовы Expect (). Когда мне нужно провести рефакторинг, я трачу больше времени на создание ложных звонков, чем на самом деле, пишу новый код. А создание реального TDD для этой функции делает ее еще более сложной, если не невозможной. Это заставляет меня думать, что даже не стоит проводить модульное тестирование этой функции верхнего уровня, так как на самом деле этот класс не выполняет много логики, он просто передает данные своим составным объектам (которые полностью протестированы и не требуют модуля). насмешливый).
Итак, стоит ли мне вообще проверять эту функцию высокого уровня? И что я получаю, делая это? Или я совершенно неправильно использую здесь макетные объекты? Возможно, мне следует отказаться от модульных тестов в этом классе, а вместо этого просто сделать автоматический интеграционный тест, который использует реальные реализации объектов и Asserts () для запросов SQL, чтобы убедиться, что существуют правильные данные конечного состояния? Что мне здесь не хватает?
РЕДАКТИРОВАТЬ: Вот код - первая функция - это метод Run () - затем мои пять тестов, которые проверяют все пять возможных путей кода. Я изменил его по причинам NDA, но общая концепция все еще там. Что-то не так с тем, как я тестирую эту функцию, какие-либо предложения по изменению, чтобы сделать ее лучше? Спасибо.