Как проверить класс, который подключается к FTP-серверу? - PullRequest
12 голосов
/ 27 марта 2012

Я разрабатываю живое обновление для своего приложения. До сих пор я создал почти все модульные тесты, но не знаю, как протестировать определенный класс, который подключается к FTP-серверу и загружает новые версии.

Чтобы протестировать этот класс, я должен создать тестовый сервер FTP и использовать его в своих модульных тестах? Если да, как я могу убедиться, что этот FTP-сервер всегда соответствует моим тестам? Должен ли я создавать вручную каждый файл, который мне понадобится до начала теста, или я должен автоматизировать его в своем классе тестирования (методы разборки и настройки)?

Этот вопрос также относится к классам модульного тестирования, которые подключаются к любому серверу.

EDIT

Я уже издеваюсь над своим классом ftp, поэтому мне не всегда нужно подключаться к серверу ftp в других тестах.

Дайте мне посмотреть, правильно ли я понял, что сказал Уоррен в своем комментарии:

Я бы сказал, что когда вы разговариваете с отдельным приложением по TCP / IP мы должны назвать это «интеграционные тесты». Один больше не тестирует единица или метод, но система.

Когда модульному тесту нужно связываться с другим приложением (это может быть HTTP-сервер или FTP-сервер), это больше не модульный тест, а сервер интеграции? Если да, я делаю это неправильно, пытаясь использовать методы модульного тестирования для создания этого теста? Правильно ли говорить, что я не должен проводить модульное тестирование этого класса? Это имеет смысл для меня, потому что, кажется, много работы для модульного теста.

Ответы [ 5 ]

9 голосов
/ 27 марта 2012

При тестировании цель всегда состоит в том, чтобы сначала ответить на вопрос: что тестируется, то есть охват теста.

Так что, если вы тестируете реализацию FTP-серверавам нужно будет создать FTP-клиент.

Если вы тестируете FTP-клиент, вам придется создать FTP-сервер.

Поэтому вам придется сократить тестрасширяйте, пока не достигнете унитарного уровня.

Это может быть, например, для вашей цели:

  • Получение списка текущих файлов, установленных для приложения;
  • Получение списка файлов, доступных удаленно;
  • Получение обновления файла;
  • Проверка правильности файла (контрольная сумма?);
  • и т. Д ...

В каждом тестируемом предмете должно быть несколько макетов и заглушек.См. эту статью о разнице между ними.Короче говоря (AFAIK) заглушка - это просто объект эмуляции, который всегда работает.И макет (который должен быть уникальным в каждом тесте) - это элемент, который может изменить результат теста (успешно или неудачно).

Для точного назначения FTP-соединения вы можете, например, (при тестировании клиента).сторона) есть некоторые заглушки, которые возвращают список файлов, и макет, который будет тестировать несколько возможных проблем с сервером FTP (тайм-аут, потеря соединения, неправильный контент).Тогда ваша клиентская сторона отреагирует так, как ожидается.Ваш макет может быть настоящим экземпляром FTP-сервера, но он будет вести себя так, как ожидалось, что вызовет все потенциальные ошибки.Как правило, каждая ошибка должна вызывать исключение, которое должно отслеживаться тестовыми блоками, чтобы пройти / провалить каждый тест.

Писать хороший тестовый код немного сложно.Сначала подход, основанный на тестировании, отнимает немного времени, но в долгосрочной перспективе он всегда лучше.Хорошая книга здесь обязательна, или, по крайней мере, некоторые справочные статьи (как у Мартина Фаулера, как указано выше).В Delphi использование интерфейсов и принципов SOLID может помочь вам в написании такого кода и создании заглушек / макетов для написания ваших тестов.

Из моего эксперимента каждый программист иногда теряется в написании тестов ... хорошее тестированиев некоторых случаях может занимать больше времени, чем написание функций ... вас предупреждают!Каждое испытание должно рассматриваться как особенность, а его стоимость должна оцениваться: стоит ли это того?Разве другой тест здесь не подходит?Мой тест отделен от функции, которую он тестирует?Разве это еще не проверено?Тестирую ли я свой код или стороннюю / библиотечную функцию?

Вне темы, но мои два цента: HTTP / 1.1 в настоящее время может быть более подходящим кандидатом, чем FTP, даже для обновления файлов.Вы можете возобновить HTTP-соединение, параллельно загружать HTTP-контент, и этот протокол более дружественный к прокси, чем FTP.И разместить HTTP-контент гораздо проще, чем FTP (некоторые FTP-серверы также имеют проблемы с безопасностью).В настоящее время большинство обновлений программного обеспечения выполняется через HTTP / 1.1, а не по FTP (например, продукты Microsoft или большинство репозиториев Linux).

РЕДАКТИРОВАТЬ:

Вы можете утверждать, что выделать интеграционные тесты, когда вы используете удаленный протокол.Это может иметь смысл, но ИМХО это не то же самое.

Насколько я понимаю, интеграционные тесты проводятся, когда все ваши компоненты работают вместе, как с реальным приложением, а затем проверяют, что они работают должным образом.Мое предложение о тестировании FTP заключается в том, что вы издеваетесь над сервером FTP, чтобы явно протестировать все потенциальные проблемы (тайм-аут, ошибка соединения или передачи ...).Это нечто иное, чем интеграционные тесты: охват кода намного больше.И вы тестируете только одну часть кода, а не всю интеграцию кода.Это не потому, что вы используете какое-то удаленное соединение, которое вы проводите интеграционные тесты: это все еще унитарное тестирование.

И, конечно, интеграционные и системные тесты должны проводиться после унитарных тестов.Но унитарные тесты клиента FTP могут имитировать сервер FTP, запускать его локально, но тестировать все потенциальные проблемы, которые могут возникнуть в реальной большой всемирной паутине.

5 голосов
/ 28 марта 2012

Если вы используете компонент TIdFTP в Indy 10, то вы можете использовать класс TIdIOHandlerStream в Indy для фальсификации FTP-соединения без физического подключения к реальному FTP-серверу.

Создание TStream объект, такой как TMemoryStream или TStringStream, который содержит ответы FTP, которые вы ожидаете получить TIdFTP для всех команд, которые он отправляет (используйте перехватчик пакетов, чтобы захватить их заранее, чтобы дать вам представление о том, что вам нужновключить), и поместите копию файла обновления в локальную папку, куда вы обычно загружаете.Создайте объект TIdIOHandlerStream и присвойте TStream в качестве ReceiveStream, затем присвойте этот IOHandler свойству TIdFTP.IOHandler перед вызовом Connect().

Например:

ResponseData := TStringStream.Create(
  '220 Welcome' + EOL +
  ... + // login responses here, etc...
  '150 Opening BINARY mode data connection for filename.ext' + EOL +
  '226 Transfer finished' + EOL +
  '221 Goodbye' + EOL);

IO := TIdIOHandlerStream.Create(FTP, ResponseData); // TIdIOHandlerStream takes ownership of ResponseData by default

FTP.IOHandler := IO;
FTP.Passive := False;  // Passive=True does not work under this setup

FTP.Connect;
try
  FTP.Get('filename.ext', 'c:\path\filename.ext');
  // copy your test update file to 'c:\path\filename.ext'...
finally
  FTP.Disconnect;
end;
4 голосов
/ 27 марта 2012

Модульные тесты должны быть быстрыми, быстро осветляться.Все, что замедляет их, отговаривает вас от желания запускать их.

Предполагается, что они также должны быть последовательными при каждом запуске.Проверка фактической передачи файлов привела бы к случайным сбоям в ваших модульных тестах.

Если класс, который вы тестируете, не делает ничего, кроме как оборачивает api библиотеки ftp, которую вы используете, то вы достигли одного изграницы вашего приложения вам не нужно единица проверить его.(Ну, иногда вы делаете. Это называется предварительным тестированием, но обычно его отбрасывают, как только вы получите ответ)

Если, однако, в классе есть какая-то логика, вы должны попробовать протестировать его отдельно отфактический api.Вы делаете это, создавая оболочку для ftp API.Затем в своих модульных тестах вы создаете двойной тест, который может заменить замену оболочки.Есть много вариантов, которые идут под разными именами: заглушка, подделка, макет объекта.Суть в том, что вы хотите убедиться, что ваши модульные тесты изолированы от любых внешних воздействий.Модульное тестирование со спорадическим поведением менее чем бесполезно.

Проверка фактического механизма передачи файлов должна выполняться в интеграционном тестировании, которое обычно запускается реже, потому что оно медленнее.Даже в интеграционном тестировании вы захотите максимально контролировать среду тестирования.(т. е. тестирование с использованием ftp-сервера в локальной сети, настроенной для имитации рабочего сервера).

И помните, что вы никогда не поймаете все сразу.Ошибки будут проскальзывать независимо от того, насколько хороши тесты.Просто убедитесь, что когда они это сделают, вы добавите еще один тест, чтобы поймать их в следующий раз.

Я бы порекомендовал либо купить, либо проверить копию Тестовых таблиц XUnit Жерара Месароса.Это клад полезной информации о том, что / когда / как проводить юнит-тест.

2 голосов
/ 27 марта 2012

Просто позаимствуйте демонстрацию FTP или HTTP-сервера, которая поставляется с любым набором компонентов сокета, который вы предпочитаете (Indy, ICS или любой другой).Сервер мгновенного тестирования.

Я бы положил его в папку с инструментами, чтобы пройти мои юнит-тесты.Я мог бы написать некоторый код, который проверяет, если TestFtpServer.exe уже жив, и если нет, запустите его.

Я бы не использовал его в оперативной памяти моего приложения модульного тестирования, то есть в отдельном процессе.

Обратите внимание, что к тому времени, когда вы перейдете к работе FTP-сервера, модульное тестирование действительно должно называться "интеграционное тестирование ".

Я бы не стал вручную создавать файлы из моего модульного теста.Я ожидаю, что мой код должен извлекать данные из системы управления версиями и собирать, как она есть, из пакетного файла, который запускает мою тестовую программу, которая знает о подпапке с именем Tools, содержащей EXE-файлы и, возможно, о папке * 1009.* и LocalData, которые можно использовать для хранения данных, которые запускаются на сервере и передаются в мое локальное приложение для тестирования модулей.Может быть, вы можете взломать ваш демонстрационный сервер, чтобы он завершил сеанс на полпути (когда вы хотите проверить ошибки), но я все еще не думаю, что вы получите хорошее покрытие.

Примечание Если вы делаете автоматические обновления, я думаю, что никакое количество модульного тестирования не снизит его.Вам нужно разобраться со множеством потенциальных проблем, связанных с интернетом.Что происходит, когда ваше имя хоста не разрешается?Что происходит, когда загрузка проходит частично и не удается?Автоматическое обновление не очень хорошо сочетается с возможностями модульного тестирования.

0 голосов
/ 27 марта 2012

Напишите пару целенаправленных интеграционных тестов для одного компонента, который знает, как взаимодействовать с FTP-сервером.Для этих тестов вам нужно будет запускать FTP-сервер перед каждым тестом, помещать туда все файлы, необходимые для теста, и после теста выключить сервер.

После этого в все остальные тесты вы не будете использовать компонент, который действительно подключается к FTP-серверу, но вы будете использовать его поддельную или фиктивную версию (которая подкреплена некоторой структурой данных в памяти вместо реальных файлов и сетевых сокетов).Таким образом, вы можете написать модульные тесты, для которых не требуется FTP-сервер или сетевое соединение, для всего остального , за исключением клиентского компонента FTP.

В дополнение к этим тестам, это может бытьТакже желательно иметь несколько сквозных тестов, которые запускают всю программу (в отличие от интеграционных тестов на уровне компонентов) и подключают настоящий FTP-сервер.Сквозные тесты не могут охватить все угловые случаи (в отличие от модульных тестов), но они могут помочь решить проблемы интеграции .

...