Каковы некоторые стратегии для тестирования больших конечных автоматов? - PullRequest
20 голосов
/ 22 апреля 2010

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

  • Enum: текущее состояние (т. Е. 0 -> 30)
  • Enum: source (в настоящее время только 2 записи)
  • Boolean: Запрос
  • Boolean: Тип
  • Enum: Status (3 состояния)
  • Enum: Обработка (3 состояния)
  • Логическое значение: Завершено

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

[Subject("Application Process States")]
public class When_state_is_meeting2Requested : AppProcessBase
{
    Establish context = () =>
    {
        //Setup....
    };

    Because of = () => process.Load(jas, vac);

    It Current_node_should_be_meeting2Requested = () => process.CurrentNode.ShouldBeOfType<meetingRequestedNode>();
    It Can_move_to_clientDeclined = () => Check(process, process.clientDeclined);
    It Can_move_to_meeting1Arranged = () => Check(process, process.meeting1Arranged);
    It Can_move_to_meeting2Arranged = () => Check(process, process.meeting2Arranged);
    It Can_move_to_Reject = () => Check(process, process.Reject);
    It Cannot_move_to_any_other_state = () => AllOthersFalse(process);
}

Никто не совсем уверен, каким должен быть выход для каждого состояния и набора входов. Я начал писать тесты для этого. Однако мне нужно написать что-то вроде 4320 тестов (30 * 2 * 2 * 2 * 3 * 3 * 2).

Какие у вас предложения по тестированию конечных автоматов?

<Ч />

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

Ответы [ 9 ]

6 голосов
/ 22 апреля 2010

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

Большая проблемная область в моих глазах:

  • Имеется 31 возможное состояние.
  • Имеет следующие входы:
    • Enum: Текущее состояние (т. Е. 0 -> 30)
    • Enum: источник (в настоящее время только 2 записи)
    • Boolean: Запрос
    • Boolean: тип
    • Enum: Status (3 состояния)
    • Enum: Обработка (3 состояния)
    • Логическое значение: Завершено

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

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

Тестирование устаревшего кода

Выезд Pex . Вы утверждаете, что унаследовали этот код, так что на самом деле это не Test-Driven-Development. Вы просто хотите, чтобы юнит-тесты охватывали каждый аспект. Это хорошо, так как любая дальнейшая работа будет подтверждена. Лично я еще не использовал Pex должным образом, однако я был поражен видео, которое я видел. По сути, он будет генерировать модульные тесты на основе входных данных, которые в этом случае были бы самим конечным автоматом. Это создаст тестовые случаи, о которых вам не хватит думать. Конечно, это не TDD, но в этом случае при тестировании устаревшего кода оно должно быть идеальным.

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

4 голосов
/ 30 апреля 2010

Тестирование на всех парах

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

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

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

Пример файла модели Pict

Данная модель генерирует 93 тестовых случая, охватывающих все парывходные параметры.

#
# This is a PICT  model for testing a complex state machine at work 
#

CurrentState  :0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
Source        :1,2
Request       :True, False
Type          :True, False
Status        :State1, State2, State3
Handling      :State1, State2, State3
Completed     :True,False

#
# One can add constraints to the model to exclude impossible 
# combinations if needed.
#
# For example:
# IF [Completed]="True" THEN CurrentState>15;
#

#
# This is the PICT output of "pict ComplexStateMachine.pict /s /r1"
#
# Combinations:    515
# Generated tests: 93
3 голосов
/ 23 апреля 2010

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

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

Вы должны использовать то, что я называю картой перехода магистрали. В восточном побережье США большинство автомагистралей называют шлагбаумами. Власти Магистрали выдают карту тарифов платной магистрали. Если в платном участке было 50 выходов, на карте ценообразования была бы таблица 50х x 50cols, в которой исчерпывающий список выходов представлен как в виде строк, так и столбцов. Чтобы узнать плату за проезд для входа в выход 20 и выхода 30, просто найдите пересечение строки 20 и столбца 30.

Для конечного автомата из 30 состояний карта перехода магистрали будет представлять собой матрицу 30 x 30, в которой перечислены все 30 возможных состояний по строкам и столбцам. Давайте определим, что строки - это ТЕКУЩИЕ состояния, а столбцы - СЛЕДУЮЩИЕ состояния.

В каждой пересекающейся ячейке будет указана «цена» перехода из состояния CURRENT (строка) в состояние NEXT (столбец). Однако вместо одного значения $ ячейка будет ссылаться на строку в таблице входов, которую мы могли бы назвать идентификатором перехода.

В разработанном мною медицинском оборудовании FSM были входные данные, такие как String, enums, int и т. Д. В таблице Inputs перечислены эти входные стимулы по столбцам.

Чтобы построить таблицу входов, вы должны написать простую процедуру для перечисления всех возможных комбинаций входов. Но стол будет огромным. В вашем случае таблица будет иметь 4320 строк и, следовательно, 4320 идентификаторов перехода. Но это не утомительная таблица, потому что вы создали таблицу программно. В моем случае я написал простую JSP для отображения таблицы ввода переходов (и таблицы пошлин) в браузере или загрузки в виде csv для отображения в MS Excel.

Существует два направления построения этих двух таблиц.

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

  2. направление теста обратное, где вы строите таблицу входов. Вы должны написать общую процедуру для исчерпывающего теста перехода.
    Сначала подпрограмма считывает таблицу последовательности переходов, чтобы привести конечный автомат в состояние точки входа, чтобы начать цикл тестирования. В каждом текущем состоянии он готов пройти через все 4320 идентификаторов переходов. В каждой строке состояний CURRENT в карте переходов Turnpike будет ограниченное число допустимых столбцов NEXT.

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

Но вы не можете - потому что, как только будет осуществлен эффективный переход, он изменит состояние машины в СЛЕДУЮЩЕЕ состояние и не позволит вам завершить тестирование остальных идентификаторов перехода в этом предыдущем состоянии CURRENT. Как только машина изменит состояние, вы должны снова начать тестирование с идентификатором перехода 0.

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

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

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

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

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

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

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

Для списка java контроллера конечного автомата http://code.google.com/p/synthfuljava/source/browse/#svn/trunk/xml/org/synthful. Тестовые процедуры не включены.

3 голосов
/ 23 апреля 2010

Использовать SpecExplorer или NModel .

3 голосов
/ 23 апреля 2010

Как вы думаете, сколько тестов необходимо, чтобы "полностью" проверить сумму функции (int a, int b)? В c # это было бы что-то вроде 18446744056529682436 тестов ... Гораздо хуже, чем в вашем случае.

Я бы предложил следующее:

  1. Проверьте большинство возможных ситуаций, граничные условия.
  2. Проверьте некоторые важные части вашего SUT отдельно.
  3. Добавить тестовые случаи, когда QA обнаружил ошибки или в производстве.

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

Start
  .UploadDocument("name1")
  .SendDocumentOnReviewTo("user1")
  .Review()
  .RejectWithComment("Not enough details")
  .AssertIsCompleted()

Пример создания простых тестов для потоков приведен здесь: http://slmoloch.blogspot.com/2009/12/design-of-selenium-tests-for-aspnet_09.html

3 голосов
/ 22 апреля 2010

Я не могу придумать какой-либо простой способ протестировать ФСМ, подобный этому, без получения действительно педантичных и использования доказательств, с использованием методов машинного обучения или грубой силы.

Грубая сила: Напишите что-нибудь, что сгенерирует все 4320 тестовых случаев каким-то декларативным образом с в основном неверными данными. Я бы порекомендовал поместить это в файл CSV, а затем использовать что-то вроде параметрического тестирования NUnits для загрузки всех тестовых случаев. Теперь большинство из этих тестовых примеров не пройдут, поэтому вам придется вручную обновить декларативный файл, чтобы он был корректным, а для случайного выбора взять только выборку тестовых случаев.

Техника машинного обучения: Вы можете использовать некоторые векторные машины или алгоритмы / эвристики MDA, чтобы попытаться изучить образец, взятый из того, что мы упомянули выше, и научить свою программу ML своему FSM. Затем запустите алгоритм на всех входах 4320 и посмотрите, где два не согласны.

1 голос
/ 23 апреля 2010

Вы могли бы рассмотреть вопрос о расследовании Основанного на модели тестирования . В таких ситуациях есть несколько инструментов, которые помогут вам с генерацией тестов. Я обычно рекомендую MBT .

1 голос
/ 23 апреля 2010

Тест на основе требований. Если определенное состояние требуется для перехода в определенное другое состояние, когда значение «Завершено» имеет значение «истина», тогда напишите тест, который автоматически циклически перебирает все комбинации других входов (это просто пара для циклов), чтобы доказать, что другие входы правильно игнорируется. Вы должны получить один тест для каждой переходной дуги, который, по моим оценкам, будет где-то порядка 100 или 150 тестов, а не 4000.

0 голосов
/ 22 апреля 2010

Грубая сила с тестами покрытия, кажется, самое начало.

...