Развитие тестовой технологии контроля уровня обученности студентов. Основы BDD: Юнит, Интеграционные и End-to-End тесты

  • 30.09.2019

Лекция является второй из трех рассматривающих уровни процесса верификации. Тема данной лекции - процесс интеграционного тестирования, его задачи и цели. Рассматриваются организационные аспекты интеграционного тестирования - структурная и временная классификации методов интеграционного тестирования, планирование интеграционного тестирования. Цель данной лекции: дать представление о процессе интеграционного тестирования, его технической и организационной составляющих

Задачи и цели интеграционного тестирования

Результатом тестирования и верификации отдельных модулей, составляющих программную систему, должно быть заключение о том, что эти модули являются внутренне непротиворечивыми и соответствуют требованиям. Однако отдельные модули редко функционируют сами по себе, поэтому следующая задача после тестирования отдельных модулей - тестирование корректности взаимодействия нескольких модулей, объединенных в единое целое. Такое тестирование называют интеграционным. Его цель удостовериться в корректности совместной работы компонент системы.

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

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

Синицын С.В., Налютин Н.Ю. Верификация программного обеспечения

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

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

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

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

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

На Рис. 15 приведена структура комплекса программ K, состоящего из оттестированных на этапе модульного тестирования модулей M1, M2, M11, M12, M21, M22. Задача, решаемая методом интеграционного тестирования, - тестирование межмодульных связей, реализующихся при исполнении программного обеспечения комплекса K.

Рис. 15. Пример структуры комплекса программ

Интеграционное тестирование использует модель «белого ящика» на модульном уровне. Поскольку тестировщику текст программы известен с детальностью до вызова всех модулей, входящих в тестируемый комплекс, применение структурных критериев на данном этапе возможно и оправдано.

Интеграционное тестирование применяется на этапе сборки модульно оттестированных модулей в единый комплекс. Известны два метода сборки модулей:

Монолитный, характеризующийся одновременным объединением всех модулей в тестируемый комплекс;

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

В инкрементальном методе выделяют две стратегии добавления модулей:

1) «Сверху вниз» и соответствующее ему восходящее тестирование.

2) «Снизу вверх» и соответственно нисходящее тестирование.

Особенности монолитного тестирования заключаются в следующем: для замены неразработанных к моменту тестирования модулей, кроме самого верхнего (модуль К на Рис. 15), необходимо дополнительно разрабатывать драйверы (test driver) и/или заглушки (stub), замещающие отсутствующие на момент сеанса тестирования модули нижних уровней.

Сравнение монолитного и интегрального подхода дает следующее.

1. Монолитное тестирование требует больших трудозатрат, связанных с дополнительной разработкой драйверов и заглушек и со сложностью идентификации ошибок, проявляющихся в пространстве собранного кода.

2. Пошаговое тестирование связано с меньшей трудоемкостью идентификации ошибок за счет постепенного наращивания объема тестируемого кода и соответственно локализации добавленной области тестируемого кода.

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

Например, порядок тестирования комплекса K (Рис. 15) при нисходящем тестировании может быть таким, как показано ниже, где тестовый набор, разработанный для модуля Mi, обозначен как XYi = (X, Y)i.

K->XYk
M1->XY1
M11->XY11
M2->XY2
M22->XY22
M21->XY21
M12->XY12

К недостаткам нисходящего тестирования следует отнести:

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

Сложность организации и разработки среды для реализации исполнения модулей в нужной последовательности;

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

Особенности восходящего тестирования заключаются в организации порядка сборки и перехода к тестированию модулей, соответствующему порядку их реализации.

Например, порядок тестирования комплекса K (Рис. 15) при нисходящем тестировании может быть следующим:

M11->XY11
M12->XY12
M1->XY1
M21->XY21
M2(M21, Stub(M22))->XY2
K(M1, M2(M21, Stub(M22)) ->XYk
M22->XY22
M2->XY2
K->XYk

А к недостаткам восходящего тестирования можно отнести:

Запаздывание проверки концептуальных особенностей тестируемого комплекса;

Необходимость в разработке и использовании драйверов.

12 ответов

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

Функциональное тестирование - это когда вы тестируете систему в соответствии с функциональными требованиями продукта. Управление продуктами/проектами обычно записывает эти данные, и QA формализует процесс того, что пользователь должен видеть и испытывать, и каков конечный результат этих процессов. В зависимости от продукта это может быть автоматизировано или нет.

Функциональное тестирование: Да, мы тестируем продукт или программное обеспечение в целом функционально независимо от того, работает ли он функционально или нет (кнопки тестирования, ссылки и т.д.)

Например: Страница входа

вы указываете имя пользователя и пароль, вы проверяете, ведет ли он вас на домашнюю страницу или нет.

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

Например: Отправка электронной почты

Вы отправляете кому-то одно сообщение, есть поток данных, а также изменение в базе данных (отправленная таблица увеличивает значение на 1)

Надеюсь, это помогло вам.

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

Единичное тестирование легко определить. Он тестирует CUT (Code Under Test ) и ничего больше. (Ну, как можно меньше.) Это значит, что это издевательства, подделки и светильники.

На другом конце спектра есть то, что многие люди называют тестированием системной интеграции. Это тестирование как можно больше, но все еще ищет ошибки в вашем собственном CUT.

Но как насчет обширного пространства между?

  • Например, что, если вы проверите немного больше, чем CUT? Что делать, если вы включили функцию Фибоначчи вместо использования приспособления, которое вы ввели? Я бы назвал это функциональное тестирование, но мир не согласен со мной.
  • Что делать, если вы включили time() или rand() ? Или что, если вы вызываете http://google.com ? Я бы назвал это тестирование системы, но опять же, я один.

Почему это имеет значение? Потому что системные тесты ненадежны. Они необходимы, но иногда они могут потерпеть неудачу по причинам, не зависящим от вас. С другой стороны, функциональные тесты всегда должны проходить, а не случайным образом; если они бывают быстрыми, их можно также использовать с самого начала, чтобы использовать Test-Driven Development без написания слишком большого количества тестов для вашей внутренней реализации. Другими словами, я думаю, что модульные тесты могут быть более сложными, чем они того стоят, и у меня хорошая компания .

Я поставил тесты на 3 оси, со всеми их нулями при модульном тестировании:

  • Функциональное тестирование: использование реального кода глубже и глубже в вашем стеке вызовов.
  • Интеграция-тестирование: выше и выше ваш стек вызовов; другими словами, тестирование вашего CUT путем запуска кода, который будет использовать его.
  • Системное тестирование: все больше и больше неповторимых операций (планировщик O/S, часы, сеть и т.д.).

Тест может легко быть все 3 в разной степени.

Функциональное тестирование: это процесс тестирования, в котором тестируются каждый компонент модуля. Например: если веб-страница содержит текстовое поле, необходимо проверить флажки радиобота, кнопок и выпадающих и т.д.

Тестирование интеграции: процесс, в котором проверяется поток данных между двумя модулями.

Интеграционное тестирование. Тестирование интеграции - это не что иное, как тестирование различных модулей. Вы должны проверить взаимосвязь между модулями. Например, вы открываете facebook, после чего вы видите страницу входа в систему после ввода идентификатора входа и пароля, вы можете видеть домашнюю страницу facebook, поэтому страница входа - это один модуль, а домашняя страница - это еще один модуль. вы должны проверять только связь между ними, когда вы вошли в систему, тогда только домашняя страница должна быть открыта, а не поле сообщения или что-то еще. Существует два основных типа интеграционного тестирования: подход TOP-DOWN и подход BOTTOM UP.

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

В тесте функционального тестирования основное внимание уделяется функциональности и вспомогательной функциональности приложения. Функциональность приложения должна работать правильно или нет.

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

Интеграционный тест: - Когда выполняется тестирование модуля и устранены проблемы с соответствующими компонентами, тогда все необходимые компоненты должны интегрироваться в одну систему, чтобы она могла выполнять операцию. После объединения компонентов системы Чтобы проверить, работает ли система правильно или нет, этот тип тестирования называется интеграционным тестированием.

Функциональное тестирование: - Тестирование в основном разделено на две категории: 1.Функциональное тестирование 2. Нефункциональное тестирование ** Функциональное тестирование: - Проверить, работает ли программное обеспечение в соответствии с требованиями пользователя или нет. ** Нефункциональное тестирование: - Чтобы проверить, соответствует ли программное обеспечение критериям качества, таким как стресс-тест, тест безопасности и т.д.

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

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

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

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

Тестирование интеграции

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

Функциональное тестирование

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

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

Интеграционное тестирование как часть большой работы

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

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

Методы сборки модулей

Монолитный метод означает, что все модули, которые в будущем будут подвергнуты интеграционному тестированию, собираются воедино одновременно. Почти наверняка возникают ситуации, когда часть тестируемого комплекса еще не готова.

В таком случае ее заменяют дополнительно разработанными "заглушками", или драйверами.

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

Инкрементальный метод включает в себя два способа добавления модулей:

  • сверху-вниз или восходящий,
  • снизу-вверх - нисходящий.

Особенности монолитного и инкрементального тестирования

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

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

Конечно же, оба способа сборки, монолитный и инкрементальный, имеют не только недостатки, но и достоинства. В первом случае появляются отличные возможности для параллельной разработки всех классов и функций, участвующих в тестировании, как на начальном этапе, так и после доработки. Пошаговый же метод менее трудозатратен: модули присоединяются постепенно, и так же постепенно обнаруживаются ошибки и дефекты. Это, как известно, позволяет сократить время на поиск таковых.

Преимущества проведения интеграционного тестирования

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

Интеграционное тестирование программного обеспечения имеет ряд преимуществ:

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

Исправление дефектов

Интеграционное тестирование завершено, однако это еще не все. Найденные ошибки фиксируются и отправляются разработчику для исправления, после чего процесс начинается заново.

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

Хотя в настоящее время и существует большое количество методов контроля качества, по-прежнему немаловажную роль играет интеграционное тестирование. Пример такого вида проверки может наглядно продемонстрировать «узкие» места при разработке программного обеспечения и документации.

Автоматизация тестирования

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

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

Однако автоматизация тестирования не может полностью заменить труд инженера по качеству, а лишь дополнить его.

Итак, интеграционное тестирование - это неотъемлемая часть разработки любого программного обеспечения и один из этапов всего процесса проверки качества продукта. Как и любой метод, он имеет ряд достоинств и недостатков, но без его применения становится невозможной качественная разработка ПО.

Из институтского курса по технологиям программирования я вынес следующую классификацию видов тестирования (критерий - степень изолированности кода). Тестирование бывает:

  • Блочное (Unit testing) - тестирование одного модуля в изоляции.
  • Интеграционное (Integration Testing) - тестирование группы взаимодействующих модулей.
  • Системное (System Testing) - тестирование системы в целом.
Классификация хорошая и понятная. Однако на практике выясняется, что у каждого вида тестирования есть свои особенности. И если их не учитывать, тестирование станивится обременительным и им не занимаются в должной мере. Здесь я собрал подходы к реальному применению различных видов тестирования. А поскольку я пишу на.NET, ссылки будут на соответствующие библиотеки.

Блочное тестирование

Блочное (модульное, unit testing) тестирование наиболее понятное для программиста. Фактически это тестирование методов какого-то класса программы в изоляции от остальной программы.

Не всякий класс легко покрыть unit тестами. При проектировании нужно учитывать возможность тестируемости и зависимости класса делать явными. Чтобы гарантировать тестируемость можно применять TDD методологию , которая предписывает сначала писать тест, а потом код реализации тестируемого метода. Тогда архитектура получается тестируемой. Распутывание зависимостей можно осуществить с помощью Dependency Injection . Тогда каждой зависимости явно сопоставляется интерфейс и явно определяется как инжектируется зависимость - в конструктор, в свойство или в метод.

Для осуществления unit тестирования существуют специальные фреймворки. Например, NUnit или тестовый фреймфорк из Visual Studio 2008. Для возможности тестирования классов в изоляции существуют специальные Mock фреймворки. Например, Rhino Mocks . Они позволяют по интерфейсам автоматически создавать заглушки для классов-зависимостей, задавая у них требуемое поведение.

По unit тестированию написано много статей. Мне очень нравится MSDN статья Write Maintainable Unit Tests That Will Save You Time And Tears , в которой хорошо и понятно рассказывается как создавать тесты, поддерживать которые со временем не становится обременительно.

Интеграционное тестирование

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

Если к нему подходить как к unit-тестированию, у которого в тестах зависимости не заменяются mock-объектами, то получаем проблемы. Для хорошего покрытия нужно написать много тестов, так как количество возможных сочетаний взаимодействующих компонент - это полиномиальная зависимость. Кроме того, unit-тесты тестируют как именно осуществляется взаимодействие (см. тестирование методом белого ящика). Из-за этого после рефакторинга, когда какое-то взаимодействие оказалось выделенным в новый класс, тесты рушатся. Нужно применять менее инвазивный метод.

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

Хорошая статья по интеграционному тестированию мне попалась лишь однажды - Scenario Driven Tests . Прочтя ее и книгу Ayende по DSL DSLs in Boo, Domain-Specific Languages in .NET у меня появилась идея как все-таки устроить интеграционное тестирование.

Идея простая. У нас есть входные данные, и мы знаем как программа должна отработать на них. Запишем эти знания в текстовый файл. Это будет спецификация к тестовым данным, в которой записано, какие результаты ожидаются от программы. Тестирование же будет определять соответствие спецификации и того, что действительно находит программа.

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

1) Допустим в присланных документах есть несколько разделов. Тогда в спецификации мы можем указать, что у разбираемого документа должны быть разделы с указанными именами:

$SectionNames = Введение, Текст статьи, Заключение, Литература

2) Другой пример. При конвертировании нужно разбивать геометрические фигуры на примитивы. Разбиение считается удачным, если в сумме все примитивы полностью покрывают оригинальную фигуру. Из присланных документов выберем различные фигуры и для них напишем свои спецификации. Факт покрываемости фигуры примитивами можно отразить так:

$IsCoverable = true

Понятно, что для проверки подобных спецификаций потребуется движок, который бы считывал спецификации и проверял их соответствие поведению программы. Я такой движок написал и остался доволен данным подходом. Скоро выложу движок в Open Source. (UPD: Выложил)

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

Системное тестирование

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

Первый подход - это использовать вариацию MVC паттерна - Passive View (вот еще хорошая статья по вариациям MVC паттерна) и формализовать взаимодействие пользователя с GUI в коде. Тогда системное тестирование сводится к тестированию Presenter классов, а также логики переходов между View. Но тут есть нюанс. Если тестировать Presenter классы в контексте системного тестирования, то необходимо как можно меньше зависимостей подменять mock объектами. И тут появляется проблема инициализации и приведения программы в нужное для начала тестирования состояние. В упомянутой выше статье Scenario Driven Tests об этом говорится подробнее.

Второй подход - использовать специальные инструменты для записи действий пользователя. То есть в итоге запускается сама программа, но щелканье по кнопкам осуществляется автоматически. Для.NET примером такого инструмента является White библиотека . Поддерживаются WinForms, WPF и еще несколько GUI платформ. Правило такое - на каждый use case пишется по скрипту, который описывает действия пользователя. Если все use case покрыты и тесты проходят, то можно сдавать систему заказчику. Акт сдачи-приемки должен подписать.