Я работаю со следующей системой:
Network Data Feed -> Third Party Nio Library -> My Objects via adapter pattern
Недавно у нас возникла проблема, когда я обновил версию библиотеки, которую использовал, что, помимо прочего, приводило к тому, что метки времени (которые возвращает сторонняя библиотека как long
) изменялись с миллисекунд после эпохи на наносекунды после эпохи.
Проблема:
Если я напишу тесты, которые макетируют объекты сторонней библиотеки, мой тест будет ошибочным, если я допустил ошибку в отношении объектов сторонней библиотеки. Например, я не осознавал, что временные метки изменили точность, что привело к необходимости изменения в модульном тесте, потому что мой макет вернул неверные данные. Это не ошибка в библиотеке , это произошло потому, что я что-то упустил в документации.
Проблема в том, что я не могу быть уверен в данных, содержащихся в этих структурах данных, потому что я не могу генерировать реальные без реального потока данных. Эти объекты большие и сложные и содержат много разных частей данных. Документация для сторонней библиотеки плохая.
Вопрос:
Как я могу настроить свои тесты, чтобы проверить это поведение? Я не уверен, что смогу решить эту проблему в модульном тесте, потому что сам тест легко может быть ошибочным. Кроме того, интегрированная система является большой и сложной, и ее легко пропустить. Например, в описанной выше ситуации я правильно настроил обработку меток времени в нескольких местах, но пропустил одно из них. Похоже, что в моем интеграционном тесте система работала в основном правильно, но когда я развернул ее в рабочей среде (в которой содержится гораздо больше данных), проблема стала очевидной.
У меня нет процесса для моих интеграционных тестов прямо сейчас. Тестирование по сути: старайтесь поддерживать модульные тесты хорошими, добавляйте больше тестов, когда что-то сломается, затем разверните на моем тестовом сервере и убедитесь, что все кажется нормальным, а затем разверните на производстве. Эта проблема с отметкой времени прошла модульные тесты, потому что макеты были созданы неправильно, затем она прошла интеграционный тест, потому что не вызывала каких-либо непосредственных, очевидных проблем. У меня нет отдела QA.
Timestamp
класс (содержащий любое представление , что они хотят) и обеспечивает названные методы (.seconds()
,.milliseconds()
,.microseconds()
,.nanoseconds()
) и, конечно же, названные конструкторы. Тогда бы не было проблем.Ответы:
Похоже, вы уже делаете должную осмотрительность. Но ...
На самом практическом уровне всегда включайте в свой набор кучу тестов интеграции с «полным циклом» для своего собственного кода и пишите больше утверждений, чем вы думаете. В частности, у вас должно быть несколько тестов, которые выполняют полный цикл create-read- [do_stuff] -validate.
И это звучит так, как будто вы уже делаете подобные вещи. Вы просто имеете дело с нестабильной и / или сложной библиотекой. И в этом случае полезно добавить несколько тестов типа «именно так работает библиотека», которые подтверждают ваше понимание библиотеки и служат примерами использования библиотеки.
Предположим, вы должны понимать и зависеть от того, как анализатор JSON интерпретирует каждый «тип» в строке JSON. Полезно и тривиально включить что-то подобное в ваш набор:
Но, во-вторых, помните, что автоматическое тестирование любого типа и практически на любом уровне точности все равно не сможет защитить вас от всех ошибок. При обнаружении проблем можно добавлять тесты . Отсутствие отдела контроля качества означает, что многие из этих проблем будут обнаружены конечными пользователями.
И в значительной степени это просто нормально.
И в-третьих, когда библиотека меняет значение возвращаемого значения или поля без переименования поля или метода или иным образом «взлома» зависимого кода (возможно, путем изменения его типа), я был бы чертовски недоволен этим издателем. И я бы сказал, что, хотя вы, вероятно, должны были прочитать журнал изменений, если таковой имеется, вы, вероятно, также должны передать часть своего стресса издателю. Я бы сказал, что им нужна конструктивная критика ...
источник
(new JSONParser()).parse(datastream)
, так как они получают данные непосредственно из a,NetworkInterface
и все классы, которые выполняют фактический анализ, являются частными пакетами и защищены.NetworkInterface
... это то, что вы можете передавать данные, подключив интерфейс к порту на localhost или что-то еще?NetworkInterface
, Это низкоуровневый объект для непосредственной работы с сетевой картой, открытия сокетов и т. Д.Краткий ответ: это сложно. Вы, вероятно, чувствуете, что нет хороших ответов, и это потому, что нет простых ответов.
Длинный ответ: Как говорит @ptyx , вам нужны системные тесты и интеграционные тесты, а также модульные тесты:
Некоторые конкретные предложения:
Я видел программирование, описанное как деятельность по изучению проблемы и пространства решения. Получить все идеально заранее, возможно, нереально, но вы можете учиться по факту. («Я исправил обработку меток времени в нескольких местах, но пропустил одно. Могу ли я изменить свои типы данных или классы, чтобы сделать обработку меток времени более явной и труднее пропустить, или сделать ее более централизованной, чтобы у меня было только одно место для изменения? Могу ли я изменить мои тесты для проверки большего количества аспектов обработки меток времени? Могу ли я упростить свою тестовую среду, чтобы упростить это в будущем? Могу ли я представить какой-нибудь инструмент, который бы упростил это, и если так, могу ли я найти такой инструмент в Google? " Так далее.)
источник
Я категорически не согласен с вами здесь. Это ошибка в библиотеке , на самом деле довольно коварная. Они изменили семантический тип возвращаемого значения, но не изменили программный тип возвращаемого значения. Это может привести к всевозможным разрушениям, особенно если это был незначительный удар по версии, но даже если это был серьезный удар.
Допустим, вместо этого библиотека возвратила тип
MillisecondsSinceEpoch
простой оболочки, которая содержитlong
. Когда они изменили его наNanosecondsSinceEpoch
значение, ваш код не смог бы скомпилироваться и, очевидно, указывал бы вам на места, где вам нужно внести изменения. Изменение не может молча испортить вашу программу.Еще лучше был бы
TimeSinceEpoch
объект, который мог бы адаптировать свой интерфейс, поскольку была добавлена большая точность, такая как добавление#toLongNanoseconds
метода рядом с#toLongMilliseconds
методом, не требуя никаких изменений в вашем коде.Следующая проблема заключается в том, что у вас нет надежного набора интеграционных тестов для библиотеки. Вы должны написать те. Лучше было бы создать интерфейс вокруг этой библиотеки, чтобы инкапсулировать ее от остальной части вашего приложения. Несколько других ответов касаются этого (и другие продолжают появляться, когда я печатаю). Интеграционные тесты должны выполняться реже, чем ваши юнит-тесты. Вот почему наличие буферного слоя помогает. Разделите ваши интеграционные тесты в отдельную область (или назовите их по-другому), чтобы вы могли запускать их по мере необходимости, но не каждый раз, когда вы запускаете свой модульный тест.
источник
...Ex()
методы в Win32API). Если это невозможно, «разорвать» контракт, переименовав функцию (или ее тип возврата), было бы лучше, чем изменить поведение.Вам нужны интеграция и системные тесты.
Модульные тесты отлично подходят для проверки того, что ваш код ведет себя так, как вы ожидаете. Как вы понимаете, это никак не оспаривает ваши предположения или гарантирует, что ваши ожидания вменяются.
Если ваш продукт мало взаимодействует с внешними системами или не взаимодействует с такими хорошо известными, стабильными и документированными системами, что их можно смело проверять (это редко случается в реальном мире) - модульных тестов недостаточно.
Чем выше уровень ваших тестов, тем больше они защитят вас от неожиданностей. Это обходится дорого (удобство, скорость, хрупкость ...), поэтому модульные тесты должны оставаться основой вашего тестирования, но вам нужны другие уровни, в том числе - в конечном итоге - крошечное человеческое тестирование, которое имеет большое значение для ловли глупые вещи, о которых никто не думал.
источник
Лучше всего было бы создать минимальный прототип и понять, как именно работает библиотека. Делая это, вы получите некоторые знания о библиотеке с плохой документацией. Прототип может быть минималистичной программой, которая использует эту библиотеку и выполняет функции.
В противном случае, нет смысла писать модульные тесты с полуопределенными требованиями и слабым пониманием системы.
Что касается вашей конкретной проблемы - об использовании неправильных метрик: я бы отнесся к этому как к изменению требований. Как только вы обнаружили проблему, измените модульные тесты и код.
источник
Если бы вы использовали популярную стабильную библиотеку, то, возможно, вы могли бы предположить, что она не будет работать против вас. Но если такие вещи, как вы описали, происходят с этой библиотекой, то, очевидно, это не одно. После этого неудачного опыта, каждый раз, когда что-то идет не так в вашем взаимодействии с этой библиотекой, вам нужно будет изучить не только вероятность того, что вы допустили ошибку, но также и вероятность того, что библиотека допустила ошибку. Итак, допустим, что это библиотека, в которой вы «не уверены».
Один из методов, используемых в библиотеках, в которых мы «не уверены», - это создание промежуточного уровня между нашей системой и указанными библиотеками, который абстрагирует функциональность, предлагаемую библиотеками, утверждает, что наши ожидания относительно библиотеки правильны, а также значительно упрощает наша жизнь в будущем, если мы решим запустить эту библиотеку и заменить ее другой библиотекой, которая ведет себя лучше.
источник
assert
ключевое слово (или функция, или средство, в зависимости от того, какой язык вы используете,) - ваш друг. Я не говорю об утверждениях в модульных / интеграционных тестах, я говорю, что уровень изоляции должен быть очень тяжелым с утверждениями, утверждая все, что можно утверждать о поведении библиотеки.