Стратегии тестирования реактивного, асинхронного кода - PullRequest
7 голосов
/ 17 июня 2010

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

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

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

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

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

  • откуда мне знать, что весь сигнал и параметры работали?
  • Нужно ли использовать тайм-ауты во время ожиданияСигнал для того, чтобы сказать, что он был излучен успешно или нет?
  • Как я могу формально быть уверенным , что Сигнал не будет излучаться, если я просто подожду немного дольше (проблема с остановкой?; -))
  • И,Как я могу быть уверен, что Сигнал испущен, потому что это был me , который установил параметр, а не другую Операцию?Вполне возможно, что мой тест приходит на ранний срок и видит сигнал, который был излучен задолго до того, как установка параметра вызвала его решение.

В настоящее время я предполагаю, что тривиальные случаи легко проверить, но как только я хочу протестировать сложные ситуации «многие ко многим» между операциями, я должен прибегнуть к надежде, что проект Just Works (tm) ...

Edit (1):

Давайте рассмотрим следующий сценарий:

Представьте себе случай, когда операция A предоставляет значение для операций B1, B2 и B3, каждая из которых имеет решение по каждому входу (то, которое выполняется всякий раз, когда любой параметробновляется).Затем пусть каждый из B1, B2 и B3 передает свое значение одному и тому же параметру операции C (скажем, чтобы объединить эти значения в справочную таблицу или что-то подобное).

Предполагаемые шаги:

  1. Сигнал о том, что он имеет новое значение (в силу своего Решения)
  2. Через некоторое время асинхронный наблюдатель отправляет сигнал любому зарегистрированному
  3. AhAccessor зарегистрирован.Вызывается его обратный вызов, который, в свою очередь, извлекает результат операции и мультиплексирует его в параметры B1, B2 и B3
  4. B1, B2 и B3 информируют об этом свое решение, что создает три новых сигнала дляНаблюдатель
  5. Через некоторое время асинхронный наблюдатель отправляет сигнал B1, затем B2, затем B3
  6. . Каждый сигнал приводит к тому, что Аксессор извлекает Результат B1 (2, 3) и передает его в C

Итак, я знаю, что в этом случае я могу издеваться, например, Решение для C, чтобы посмотреть, действительно ли оно получило информацию о том, что сделали B1, B2 и B3.Вопрос в следующем: когда можно ли это проверить?

Редактировать (2): Моя цель больше похожа на сквозное тестирование, то есть сбор различных частейDSL и посмотрите, будет ли результат вести себя так, как я ожидаю.

Редактировать (3): Оказывается, я слишком усложнял вещи: -)

Ответы [ 2 ]

2 голосов
/ 18 июня 2010

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

Примечание: это объяснение предполагает, что вы используете принципыинверсии зависимостей, а также библиотеки-насмешки (например, Rhino Mocks).

Вы заявляете:

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

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

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

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

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

1 голос
/ 18 июня 2010

Я не использовал это, но слышал, что Reactive Framework можно использовать для преобразования событий в операторы Linq, которые затем можно использовать для упрощения модульного тестирования.

Я полагаю, что они проводят модульное тестирование большого количества кода Silverlight - например, среда Reactive распространяется с набором инструментов Silverlight (System.Reactive.dll).

...