Сообщение Re[5]: О "наивном" DI и об архитектурном бессилии от 21.09.2016 2:19
Изменено 21.09.2016 2:47 Mystic Artifact
Здравствуйте, Lexey, Вы писали:
L>Может у тебя специфика такая. У меня, как правило, ситуация обратная. Юнит-тесты писать проще и дешевле, чем интеграционные. И исполняются они на порядки быстрее. А бывало и так, что хороший интеграционный тест в принципе невозможен. И самое интересное вылезает в процессе пуско-наладки.
Я извиняюсь, что подымаю старую тему. Но в случае юнит-тестов — ситуация — все тесты зеленые, а оно не работает — вполне себе. А в процессе пуско-наладки оно не работает и как выясняется — работать не могло, по причине, например отсутствия самого важного куска в SQL процедуре.
Интеграционные тесты в каком-то смысле необходимо воспринимать как функциональные, например:
1. Компилятор для определенного входа, должен генерировать определенный код; или выдавать строго определенные ошибки в сторого определенных случаях/позициях.
2. Браузер должен ренедерить pixel-to-pixel картинку для заранее определенных/подготовленных данных.
Это: легко реализуется, ну и именно в том числе такие подходы используются в реальных проектах.
Плюсы очевидны — мы работаем на вполне определенных входных данных, кстати, что важно — они вполне читаемы человеком (повезло).
Безусловно есть совершенно другой класс систем, где входы/выходы и состояние настолько запрятаны, что протестировать так просто... просто не получится. Или входы/выходы едва анализируются человеком.
Ну вот пример на который натыкался лично я: процессинг карт/транзакций на самом деле за 20 лет никогда не считал комиссию по терминалу в соответствии со спецификацией — она должна была быть взята в соответствии с приказом на терминал на дату транзакции. На самом деле считалось по последнему приказу (точнее в соответствии с приказом соответствующий текущей дате). В случае задержки обработки по тем или иным причинам реальный разрыв времени / сбой может произойти. Ситуация как уже понятно весьма и весьма редкая, тем не менее поизошла.
И тут мы имеем, допустим классику: и сервер приложений, и терминальный сервер и SQL сервер и даже где-то в стороне ОДБ.
Так вот, пока мы не наделим всей информацией все подсистемы для моделирования одного такого кейса — смоделировать по сути не получится.
Как только мы начнем наделять *каждую* подсистему необходимыми данными перед проведением теста — тест можно будет выполнить с минимальным количеством проверок / иногда даже не заглядывая во все подсистемы. В данном случае — всё вообще можно было сделать со стороны терминального сервера эмулируя команды и карт и других процессингов, что в итоге может быть проще — а может быть и нет (зависит от тестовой инфраструктуры).
Детали я маленько забыл, давно с этим уже не работаю.
И если бы я это делал изначально — я забил бы болт на все, и максимально обложил бы тестами как раз с этой стороны — потому что в момент переписывания одной из подсистем делал в общем-то тоже самое, только в меньшем объеме, вдобавок вскрыл моменты которые спецификацией не определены, что не могло бы быть незамеченным, если бы такие тесты проводились.
Безусловно это требует усилий, и немалых, однако большинство из них просто технические, но главный — это как раз создание вменяемых сценариев и вменяемое воплощение их в кодируемый вид (для меня прямой кодинг для C# не работал от слова вообще — слишком много шума, DSL получше — но тут тоже дорожка тонкая — захочется отойти в сторону — он будет или слишком сложным или вернемся к C#).
А, ещё был опыт с моками и везде DI. Подход/тесты выходили настолько дебильные и большие, что легче бы сделали интеграционные — вдобавок система *полностью* наблюдаема через одну точку доступа, и фэйковый поставщик данных. Вдобавок прямо перед этим я в своем проекте делал именно так, и подход себя зарекомендовал вполне сносно. Толку явно больше, потому как довольно просто описывается, вместо кода для моков в которых опять таки можно ошибиться, а затем допиливание самого кода что бы оно могло быть протестировано моками. Фе.
Сейчас у меня (не мой личный, скорее неофициально прижившийся) — фокус на фичи, и смотрим на error rate и выборчно результаты (их можно глазами осознать / довольно легко проверить), может быть несколько деплоев в день. Впрочем для кое-каких подсистем, пришлось написать свои интеграционные тесты. Совсем мало юнит. Впрочем для тестов — и кастом/MITM прокси, позволяющая "украсть" ответ запроса и потом бесконечно его тыркать (для перфоманс тестов), да и небольшой in-house фреймворк. Кое-где я писал. Просто, мы, как и многие зависим от third party практически константно — и важно, что оно или работает в обозначенных сценариях — или нет. Юниты тут как бы тоже никчему не прилепишь, да и выполнить практически нормальный workflow проще. Скорее тесты — по необходимости, но не более, или там где я ожидаю изменение поведения в будущем, и хочу быть уверен, что функциональный блок сохранит работоспособность, вплоть до ассерта в рантайме. Ну, а встреченных проблем кстати в third party — хватает, начиная от самого фреймворка, заканчивания каким-нибудь клиентом к rabbit который в асинхронном методе синхронно лочится в BlockingCollection, чем ставит раком абсолютно всю систему, хотя и не должен бы. Кватовая физика прямо — попытки пронаблюдать состояние в дебаггере — приводят к тому, что tasks "отлипают".
Я в сущности согласен, что всё — зависит от специфики. Самое важное — знать спецификацию/сценарии, с этим у меня обычно проблемы — их ведь обычно нет и сценарии нужно придумывать — а это и есть самое сложное/затратное (имхо), для любых видов тестирования.
PS: Кстати сейчас проект практически без DI. Но кое-где напрашивается.
L>Может у тебя специфика такая. У меня, как правило, ситуация обратная. Юнит-тесты писать проще и дешевле, чем интеграционные. И исполняются они на порядки быстрее. А бывало и так, что хороший интеграционный тест в принципе невозможен. И самое интересное вылезает в процессе пуско-наладки.
Я извиняюсь, что подымаю старую тему. Но в случае юнит-тестов — ситуация — все тесты зеленые, а оно не работает — вполне себе. А в процессе пуско-наладки оно не работает и как выясняется — работать не могло, по причине, например отсутствия самого важного куска в SQL процедуре.
Интеграционные тесты в каком-то смысле необходимо воспринимать как функциональные, например:
1. Компилятор для определенного входа, должен генерировать определенный код; или выдавать строго определенные ошибки в сторого определенных случаях/позициях.
2. Браузер должен ренедерить pixel-to-pixel картинку для заранее определенных/подготовленных данных.
Это: легко реализуется, ну и именно в том числе такие подходы используются в реальных проектах.
Плюсы очевидны — мы работаем на вполне определенных входных данных, кстати, что важно — они вполне читаемы человеком (повезло).
Безусловно есть совершенно другой класс систем, где входы/выходы и состояние настолько запрятаны, что протестировать так просто... просто не получится. Или входы/выходы едва анализируются человеком.
Ну вот пример на который натыкался лично я: процессинг карт/транзакций на самом деле за 20 лет никогда не считал комиссию по терминалу в соответствии со спецификацией — она должна была быть взята в соответствии с приказом на терминал на дату транзакции. На самом деле считалось по последнему приказу (точнее в соответствии с приказом соответствующий текущей дате). В случае задержки обработки по тем или иным причинам реальный разрыв времени / сбой может произойти. Ситуация как уже понятно весьма и весьма редкая, тем не менее поизошла.
И тут мы имеем, допустим классику: и сервер приложений, и терминальный сервер и SQL сервер и даже где-то в стороне ОДБ.
Так вот, пока мы не наделим всей информацией все подсистемы для моделирования одного такого кейса — смоделировать по сути не получится.
Как только мы начнем наделять *каждую* подсистему необходимыми данными перед проведением теста — тест можно будет выполнить с минимальным количеством проверок / иногда даже не заглядывая во все подсистемы. В данном случае — всё вообще можно было сделать со стороны терминального сервера эмулируя команды и карт и других процессингов, что в итоге может быть проще — а может быть и нет (зависит от тестовой инфраструктуры).
Детали я маленько забыл, давно с этим уже не работаю.
И если бы я это делал изначально — я забил бы болт на все, и максимально обложил бы тестами как раз с этой стороны — потому что в момент переписывания одной из подсистем делал в общем-то тоже самое, только в меньшем объеме, вдобавок вскрыл моменты которые спецификацией не определены, что не могло бы быть незамеченным, если бы такие тесты проводились.
Безусловно это требует усилий, и немалых, однако большинство из них просто технические, но главный — это как раз создание вменяемых сценариев и вменяемое воплощение их в кодируемый вид (для меня прямой кодинг для C# не работал от слова вообще — слишком много шума, DSL получше — но тут тоже дорожка тонкая — захочется отойти в сторону — он будет или слишком сложным или вернемся к C#).
А, ещё был опыт с моками и везде DI. Подход/тесты выходили настолько дебильные и большие, что легче бы сделали интеграционные — вдобавок система *полностью* наблюдаема через одну точку доступа, и фэйковый поставщик данных. Вдобавок прямо перед этим я в своем проекте делал именно так, и подход себя зарекомендовал вполне сносно. Толку явно больше, потому как довольно просто описывается, вместо кода для моков в которых опять таки можно ошибиться, а затем допиливание самого кода что бы оно могло быть протестировано моками. Фе.
Сейчас у меня (не мой личный, скорее неофициально прижившийся) — фокус на фичи, и смотрим на error rate и выборчно результаты (их можно глазами осознать / довольно легко проверить), может быть несколько деплоев в день. Впрочем для кое-каких подсистем, пришлось написать свои интеграционные тесты. Совсем мало юнит. Впрочем для тестов — и кастом/MITM прокси, позволяющая "украсть" ответ запроса и потом бесконечно его тыркать (для перфоманс тестов), да и небольшой in-house фреймворк. Кое-где я писал. Просто, мы, как и многие зависим от third party практически константно — и важно, что оно или работает в обозначенных сценариях — или нет. Юниты тут как бы тоже никчему не прилепишь, да и выполнить практически нормальный workflow проще. Скорее тесты — по необходимости, но не более, или там где я ожидаю изменение поведения в будущем, и хочу быть уверен, что функциональный блок сохранит работоспособность, вплоть до ассерта в рантайме. Ну, а встреченных проблем кстати в third party — хватает, начиная от самого фреймворка, заканчивания каким-нибудь клиентом к rabbit который в асинхронном методе синхронно лочится в BlockingCollection, чем ставит раком абсолютно всю систему, хотя и не должен бы. Кватовая физика прямо — попытки пронаблюдать состояние в дебаггере — приводят к тому, что tasks "отлипают".
Я в сущности согласен, что всё — зависит от специфики. Самое важное — знать спецификацию/сценарии, с этим у меня обычно проблемы — их ведь обычно нет и сценарии нужно придумывать — а это и есть самое сложное/затратное (имхо), для любых видов тестирования.
PS: Кстати сейчас проект практически без DI. Но кое-где напрашивается.
Re[5]: О "наивном" DI и об архитектурном бессилии
Здравствуйте, Lexey, Вы писали:
L>Может у тебя специфика такая. У меня, как правило, ситуация обратная. Юнит-тесты писать проще и дешевле, чем интеграционные. И исполняются они на порядки быстрее. А бывало и так, что хороший интеграционный тест в принципе невозможен. И самое интересное вылезает в процессе пуско-наладки.
Я извиняюсь, что подымаю старую тему. Но в случае юнит-тестов — ситуация — все тесты зеленые, а оно не работает — вполне себе. А в процессе пуско-наладки оно не работает и как выясняется — работать не могло, по причине, например отсутствия самого важного куска в SQL процедуре.
Интеграционные тесты в каком-то смысле необходимо воспринимать как функциональные, например:
1. Компилятор для определенного входа, должен генерировать определенный код; или выдавать строго определенные ошибки в сторого определенных случаях/позициях.
2. Браузер должен ренедерить pixel-to-pixel картинку для заранее определенных/подготовленных данных.
Это: легко реализуется, ну и именно в том числе такие подходы используются в реальных проектах.
Плюсы очевидны — мы работаем на вполне определенных входных данных, кстати, что важно — они вполне читаемы человеком (повезло).
Безусловно есть совершенно другой класс систем, где входы/выходы и состояние настолько запрятаны, что протестировать так просто... просто не получится. Или входы/выходы едва анализируются человеком.
Ну вот пример на который натыкался лично я: процессинг карт/транзакций на самом деле за 20 лет никогда не считал комиссию по терминалу в соответствии со спецификацией — она должна была быть взята в соответствии с приказом на терминал на дату транзакции. На самом деле считалось по последнему приказу (точнее в соответствии с приказом соответствующий текущей дате). В случае задержки обработки по тем или иным причинам реальный разрыв времени / сбой может произойти. Ситуация как уже понятно весьма и весьма редкая, тем не менее поизошла.
И тут мы имеем, допустим классику: и сервер приложений, и терминальный сервер и SQL сервер и даже где-то в стороне ОДБ.
Так вот, пока мы не наделим всей информацией все подсистемы для моделирования одного такого кейса — смоделировать по сути не получится.
Как только мы начнем наделять *каждую* подсистему необходимыми данными перед проведением теста — тест можно будет выполнить с минимальным количеством проверок / иногда даже не заглядывая во все подсистемы. В данном случае — всё вообще можно было сделать со стороны терминального сервера эмулируя команды и карт и других процессингов, что в итоге может быть проще — а может быть и нет (зависит от тестовой инфраструктуры).
Детали я маленько забыл, давно с этим уже не работаю.
И если бы я это делал изначально — я забил бы болт на все, и максимально обложил бы тестами как раз с этой стороны — потому что в момент переписывания одной из подсистем делал в общем-то тоже самое, только в меньшем объеме, вдобавок вскрыл моменты которые спецификацией не определены, что не могло бы быть незамеченным, если бы такие тесты проводились.
Безусловно это требует усилий, и немалых, однако большинство из них просто технические, но главный — это как раз создание вменяемых сценариев и вменяемое воплощение их в кодируемый вид (для меня прямой кодинг для C# не работал от слова вообще — слишком много шума, DSL получше — но тут тоже дорожка тонкая — захочется отойти в сторону — он будет или слишком сложным или вернемся к C#).
А, ещё был опыт с моками и везде DI. Подход/тесты выходили настолько дебильные и большие, что легче бы сделали интеграционные — вдобавок система *полностью* наблюдаема через одну точку доступа, и фэйковый поставщик данных. Вдобавок прямо перед этим я в своем проекте делал именно так, и подход себя зарекомендовал вполне сносно. Толку явно больше, потому как довольно просто описывается, вместо кода для моков в которых опять таки можно ошибиться, а затем допиливание самого кода что бы оно могло быть протестировано моками. Фе.
Сейчас у меня (не мой личный, скорее неофициально прижившийся) — фокус на фичи, и смотрим на error rate и выборчно результаты (их можно глазами осознать / довольно легко проверить), может быть несколько деплоев в день. Впрочем для кое-каких подсистем, пришлось написать свои интеграционные тесты. Совсем мало юнит. Впрочем для тестов — и кастом/MITM прокси, позволяющая "украсть" ответ запроса и потом бесконечно его тыркать (для перфоманс тестов), да и небольшой in-house фреймворк. Кое-где я писал. Просто, мы, как и многие зависим от third party практически константно — и важно, что оно или работает в обозначенных сценариях — или нет. Юниты тут как бы тоже никчему не прилепишь, да и выполнить практически нормальный workflow проще. Скорее тесты — по необходимости, но не более, или там где я ожидаю изменение поведения в будущем, и хочу быть уверен, что функциональный блок сохранит работоспособность, вплоть до ассерта в рантайме. Ну, а встреченных проблем кстати в third party — хватает, начиная от самого фреймворка, заканчивания каким-нибудь клиентом к rabbit который в асинхронном методе синхронно лочится в BlockingCollection, чем ставит раком абсолютно всю систему, хотя и не должен бы. Кватовая физика прямо — попытки пронаблюдать состояние в дебаггере — приводят к тому, что tasks "отлипают".
Я в сущности согласен, что всё — зависит от специфики. Самое важное — знать спецификацию/сценарии, с этим у меня обычно проблемы — их ведь обычно нет и сценарии нужно придумывать — а это и есть самое сложное/затратное (имхо), для любых видов тестирования.
PS: Кстати сейчас проект практически без DI. Но кое-где напрашивается.
Update: Кстати, есть один немаловажный момент, — если система (программный комплекс) предполагает долгую жизнь, то интеграционные тесты (в случае их хорошего исполнения) позволяют полностью абстрагироваться от конкретной базы кода (понятно, что если это позволяет предмет, и в необходимых количествах), что сыграет наруку в будущем. Таким образом при необходимости миграции с продукта версии 1 к версии 2 (в современном версионировании браузеров — от перехода от версии к 70 к версии 230), где версия >200 по праву считается совершенно отдельным и независимым продуктом (т.е. переписан с "нуля", стал например распределенным, или же использует несовместимое хранилище (т.е. нет возможности апгрейда, есть возможность конверсии, при чем не всегда обратимой)) — интеграционные/функциональные тесты помогут ответить на вопрос клиента: не станет ли всё раком после перехода. А программиты которые работали над версиией 200 — смогут ответить себе на вопрос — будет ли это точно работать именно у этого клиента. Поскольку я лично с этим столкнулся, наверное поэтому считаю, что приоритет для интеграционных и функциональных тестов, по минимум юнит, оставляя им место, где просмотрев код — неясно работает ли он. Это обычно всякие парсеры и/или мудреная/перераспухшая логика, и/или где неясны граничные моменты а пощупать их хочется (и есть возможность). Впрочем перераспухей логики скорее всего как раз юнит и не помогут.
L>Может у тебя специфика такая. У меня, как правило, ситуация обратная. Юнит-тесты писать проще и дешевле, чем интеграционные. И исполняются они на порядки быстрее. А бывало и так, что хороший интеграционный тест в принципе невозможен. И самое интересное вылезает в процессе пуско-наладки.
Я извиняюсь, что подымаю старую тему. Но в случае юнит-тестов — ситуация — все тесты зеленые, а оно не работает — вполне себе. А в процессе пуско-наладки оно не работает и как выясняется — работать не могло, по причине, например отсутствия самого важного куска в SQL процедуре.
Интеграционные тесты в каком-то смысле необходимо воспринимать как функциональные, например:
1. Компилятор для определенного входа, должен генерировать определенный код; или выдавать строго определенные ошибки в сторого определенных случаях/позициях.
2. Браузер должен ренедерить pixel-to-pixel картинку для заранее определенных/подготовленных данных.
Это: легко реализуется, ну и именно в том числе такие подходы используются в реальных проектах.
Плюсы очевидны — мы работаем на вполне определенных входных данных, кстати, что важно — они вполне читаемы человеком (повезло).
Безусловно есть совершенно другой класс систем, где входы/выходы и состояние настолько запрятаны, что протестировать так просто... просто не получится. Или входы/выходы едва анализируются человеком.
Ну вот пример на который натыкался лично я: процессинг карт/транзакций на самом деле за 20 лет никогда не считал комиссию по терминалу в соответствии со спецификацией — она должна была быть взята в соответствии с приказом на терминал на дату транзакции. На самом деле считалось по последнему приказу (точнее в соответствии с приказом соответствующий текущей дате). В случае задержки обработки по тем или иным причинам реальный разрыв времени / сбой может произойти. Ситуация как уже понятно весьма и весьма редкая, тем не менее поизошла.
И тут мы имеем, допустим классику: и сервер приложений, и терминальный сервер и SQL сервер и даже где-то в стороне ОДБ.
Так вот, пока мы не наделим всей информацией все подсистемы для моделирования одного такого кейса — смоделировать по сути не получится.
Как только мы начнем наделять *каждую* подсистему необходимыми данными перед проведением теста — тест можно будет выполнить с минимальным количеством проверок / иногда даже не заглядывая во все подсистемы. В данном случае — всё вообще можно было сделать со стороны терминального сервера эмулируя команды и карт и других процессингов, что в итоге может быть проще — а может быть и нет (зависит от тестовой инфраструктуры).
Детали я маленько забыл, давно с этим уже не работаю.
И если бы я это делал изначально — я забил бы болт на все, и максимально обложил бы тестами как раз с этой стороны — потому что в момент переписывания одной из подсистем делал в общем-то тоже самое, только в меньшем объеме, вдобавок вскрыл моменты которые спецификацией не определены, что не могло бы быть незамеченным, если бы такие тесты проводились.
Безусловно это требует усилий, и немалых, однако большинство из них просто технические, но главный — это как раз создание вменяемых сценариев и вменяемое воплощение их в кодируемый вид (для меня прямой кодинг для C# не работал от слова вообще — слишком много шума, DSL получше — но тут тоже дорожка тонкая — захочется отойти в сторону — он будет или слишком сложным или вернемся к C#).
А, ещё был опыт с моками и везде DI. Подход/тесты выходили настолько дебильные и большие, что легче бы сделали интеграционные — вдобавок система *полностью* наблюдаема через одну точку доступа, и фэйковый поставщик данных. Вдобавок прямо перед этим я в своем проекте делал именно так, и подход себя зарекомендовал вполне сносно. Толку явно больше, потому как довольно просто описывается, вместо кода для моков в которых опять таки можно ошибиться, а затем допиливание самого кода что бы оно могло быть протестировано моками. Фе.
Сейчас у меня (не мой личный, скорее неофициально прижившийся) — фокус на фичи, и смотрим на error rate и выборчно результаты (их можно глазами осознать / довольно легко проверить), может быть несколько деплоев в день. Впрочем для кое-каких подсистем, пришлось написать свои интеграционные тесты. Совсем мало юнит. Впрочем для тестов — и кастом/MITM прокси, позволяющая "украсть" ответ запроса и потом бесконечно его тыркать (для перфоманс тестов), да и небольшой in-house фреймворк. Кое-где я писал. Просто, мы, как и многие зависим от third party практически константно — и важно, что оно или работает в обозначенных сценариях — или нет. Юниты тут как бы тоже никчему не прилепишь, да и выполнить практически нормальный workflow проще. Скорее тесты — по необходимости, но не более, или там где я ожидаю изменение поведения в будущем, и хочу быть уверен, что функциональный блок сохранит работоспособность, вплоть до ассерта в рантайме. Ну, а встреченных проблем кстати в third party — хватает, начиная от самого фреймворка, заканчивания каким-нибудь клиентом к rabbit который в асинхронном методе синхронно лочится в BlockingCollection, чем ставит раком абсолютно всю систему, хотя и не должен бы. Кватовая физика прямо — попытки пронаблюдать состояние в дебаггере — приводят к тому, что tasks "отлипают".
Я в сущности согласен, что всё — зависит от специфики. Самое важное — знать спецификацию/сценарии, с этим у меня обычно проблемы — их ведь обычно нет и сценарии нужно придумывать — а это и есть самое сложное/затратное (имхо), для любых видов тестирования.
PS: Кстати сейчас проект практически без DI. Но кое-где напрашивается.
Update: Кстати, есть один немаловажный момент, — если система (программный комплекс) предполагает долгую жизнь, то интеграционные тесты (в случае их хорошего исполнения) позволяют полностью абстрагироваться от конкретной базы кода (понятно, что если это позволяет предмет, и в необходимых количествах), что сыграет наруку в будущем. Таким образом при необходимости миграции с продукта версии 1 к версии 2 (в современном версионировании браузеров — от перехода от версии к 70 к версии 230), где версия >200 по праву считается совершенно отдельным и независимым продуктом (т.е. переписан с "нуля", стал например распределенным, или же использует несовместимое хранилище (т.е. нет возможности апгрейда, есть возможность конверсии, при чем не всегда обратимой)) — интеграционные/функциональные тесты помогут ответить на вопрос клиента: не станет ли всё раком после перехода. А программиты которые работали над версиией 200 — смогут ответить себе на вопрос — будет ли это точно работать именно у этого клиента. Поскольку я лично с этим столкнулся, наверное поэтому считаю, что приоритет для интеграционных и функциональных тестов, по минимум юнит, оставляя им место, где просмотрев код — неясно работает ли он. Это обычно всякие парсеры и/или мудреная/перераспухшая логика, и/или где неясны граничные моменты а пощупать их хочется (и есть возможность). Впрочем перераспухей логики скорее всего как раз юнит и не помогут.