Здравствуйте, Аноним, Вы писали:
А>Какие юнит тесты можно написать для метода — не отсыкайте к теории просто накидайте тестов.
А>Метод: А>decimal GetBalance(int accountId) ??
В такой постановке вопроса — пройтись по всем имеющимся экаунтам. Покажите реализацию или, ещё лучше, спецификацию метода — как он должен себя вести в зависимости от входного параметра, вернее, от данных экаунта, который был передан.
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Unit Tests
От:
Аноним
Дата:
19.06.11 21:12
Оценка:
Реализации нет. Все что есть — вышеназванный интерфейс. я даже не знаю какие експешены кидаются и является ли -1 валидным номером или нет. Номеров экаунтов нет, а даже если были бы — был ли бы это валидный юнит тест? Я правильно понял что просто вызвать метод для известно-валидный экаунт айдиз?
А>Реализации нет. Все что есть — вышеназванный интерфейс. я даже не знаю какие експешены кидаются и является ли -1 валидным номером или нет. Номеров экаунтов нет, а даже если были бы — был ли бы это валидный юнит тест? Я правильно понял что просто вызвать метод для известно-валидный экаунт айдиз?
Как вы себе представляете тестирование чего-либо, что даже неизвестно как работает? Если нет спецификации, то результат "10" будет ли правильным для входного параметра "25"?
Что именно предлагается потестировать в таком разе? Это что, какая-то лабораторная работа? Или квест?
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Unit Tests
От:
Аноним
Дата:
19.06.11 22:14
Оценка:
это запрос от американских коллег. нужно написать тесть of the service behavior by knowing its ineterface. я тоже не совсем понимаю как писать юнит тесты для таких задач в принципе. нам нужно привязывать ресультаты к базе данных? наопример счет 5 — бланс 10, а счет 20 баланс -8. Или что? Хорошый тест должне тестировать поведение а не имплементацию. Можно что то раскрутить от этого?
Здравствуйте, Аноним, Вы писали:
А>это запрос от американских коллег. нужно написать тесть of the service behavior by knowing its ineterface. я тоже не совсем понимаю как писать юнит тесты для таких задач в принципе. нам нужно привязывать ресультаты к базе данных? наопример счет 5 — бланс 10, а счет 20 баланс -8. Или что? Хорошый тест должне тестировать поведение а не имплементацию. Можно что то раскрутить от этого?
Давайте уж вы внимательно прочтёте для начала Howtoask. А потом приведёте дословно то, что же вас просят сделать. В том, что вы по-английски процитировали ни слова про юнит-тест нет.
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Unit Tests
От:
Аноним
Дата:
19.06.11 22:22
Оценка:
***
I beg your pardon, won't happen no more.
Вот то что надо: Write a unit test to test the behaviour of the *** method.
Здравствуйте, Аноним, Вы писали:
А>Вот то что надо: Write a unit test to test the behaviour of the *** method.
Похоже, это что-то вроде удалённого собеседования
Или давайте сюда контакты вашего рабюотодателя [] или спросите его о спецификации на данный метод. Если спеки нет и не может по каким-то причинам быть, то стратегия такая:
* Включаем соображалку, запасаемся шапочкой из фольги и кофейной гущей и пытаемся представить себе, как должен работать метод. Если говорить о методе из стартового сообщения, то не имеется ли там в виду какая-то конкретная база данных? Не может ли быть так, что GetBalance — просто селектит одно единственное поле из БД без всяких расчётов?
* Включаем холодный рассудок — может баланс быть нулевым? А отрицательным? Может. Есть ли среди значений типа decimal такой, который не может быть значением баланса? Кажется, нет. Значит, если метод запросили с невалидным (например, отсутствующим в БД) номером экаунта, метод должен бросить исключение — у метода нет другого [разумного] способа оповестить внешний мир о том, что метод не смог сосчитать баланс.
На этом месте предлагается стапятидесятиграммировать и выдумать ещё что-либо интересное. Но уже самостоятельно.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Как вы себе представляете тестирование чего-либо, что даже неизвестно как работает? Если нет спецификации, то результат "10" будет ли правильным для входного параметра "25"? _FR>Что именно предлагается потестировать в таком разе? Это что, какая-то лабораторная работа? Или квест?
Кстати, на сайте PEX как раз есть такие квесты-загадки: надо при помощи результатов юнит-тестов воспроизвести реализацию загаданного метода
1) AccountID = 0, 1, -1, MaxInt, MaxInt — 1, MinInt, MinInt + 1.
2) Дважды вызвать GetBalance, результаты должны совпадать.
3) Создать account, в котором balance = 0, MaxDecimal, MinDecimal и проверить что вызов GetBalance возвращает именно это значение.
4) Создать 10 потоков, в каждом из которых вызывать GetBalance для одного и того же accountId, для random accountId.
5) Вызывать GetBalance для последовательно увеличивающихся accountId и для последовательно уменьшающихся accountId.
Здравствуйте, Аноним, Вы писали:
А>Метод: А>decimal GetBalance(int accountId) ??
Метод сам по себе тестировать нельзя — в любом случае будет создан экземпляр объекта. Этому объекту надо подсунуть базу-заглушку с несколькими известными аккаунтами и убедиться, что метод возвращает заранее известные значения, что возвращает то что надо для несуществующих ID, что не имеет побочных эффектов.
Здравствуйте, abibok, Вы писали:
А>>decimal GetBalance(int accountId) ??
A>1) AccountID = 0, 1, -1, MaxInt, MaxInt — 1, MinInt, MinInt + 1. A>2) Дважды вызвать GetBalance, результаты должны совпадать. A>3) Создать account, в котором balance = 0, MaxDecimal, MinDecimal и проверить что вызов GetBalance возвращает именно это значение. A>4) Создать 10 потоков, в каждом из которых вызывать GetBalance для одного и того же accountId, для random accountId.
Разумно. По крайней мере это тесты, которые имеют смысл практически при любой реализации метода. Единственно что — это уже не похоже именно на юнит-тесты, это функциональные тесты для тестового окружения.
A>5) Вызывать GetBalance для последовательно увеличивающихся accountId и для последовательно уменьшающихся accountId.
Вот этот пункт непонятен. В чём специфика такой проверки?
Здравствуйте, Аноним, Вы писали:
А>это запрос от американских коллег. нужно написать тесть of the service behavior by knowing its ineterface. я тоже не совсем понимаю как писать юнит тесты для таких задач в принципе. нам нужно привязывать ресультаты к базе данных? наопример счет 5 — бланс 10, а счет 20 баланс -8. Или что? Хорошый тест должне тестировать поведение а не имплементацию. Можно что то раскрутить от этого?
Здравствуйте, abibok, Вы писали:
А>>decimal GetBalance(int accountId) ??
A>1) AccountID = 0, 1, -1, MaxInt, MaxInt — 1, MinInt, MinInt + 1.
не совсем понятно что тут тестируется
A>2) Дважды вызвать GetBalance, результаты должны совпадать.
не факт. пусть баланс функция от времени (почему нет? и такое бывает)
A>3) Создать account, в котором balance = 0, MaxDecimal, MinDecimal и проверить что вызов GetBalance возвращает именно это значение.
тоже не факт. балансы разные бывают. может, там транзакции пендятся. типа нам бабло зачисляют, но все никак не зачислят, то GetBalance его вернет. так же в тут же созданном акке может быть списане таньги за его создание (почему не может? очень даже может) или поклажа бонусов на счет.
вы уверены, что есть функция SetBalance? вот это точно бред. функция создать акк -- может быть. функция положить бабло -- тоже. функция списать бабло, вычислить проценты -- да. но прямой доступ к балансу на запись -- откуда он?
A>5) Вызывать GetBalance для последовательно увеличивающихся accountId и для последовательно уменьшающихся accountId.
и что это даст?
без спецификации тестировать тут нечего, ибо хз что считать нормальным поведением.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
A>>1) AccountID = 0, 1, -1, MaxInt, MaxInt — 1, MinInt, MinInt + 1. М>не совсем понятно что тут тестируется
Это типичный boundary check для параметра типа int, значения на которых вероятнее всего получить баг.
A>>2) Дважды вызвать GetBalance, результаты должны совпадать. М>не факт. пусть баланс функция от времени (почему нет? и такое бывает)
Мы не имеем никаких сведений о внутреннем устройстве функции, поэтому не будем усложнять. Считаем что за короткое время между двумя последовательными вызовами состояние системы не изменяется. Вообще эти тесты — не production quality code, а скорее набор идей, которые первыми приходят в голову. В реальных условиях этот набор может быть дополнен/изменен/уточнен. А пока наша цель застолбить основные направления тестирования.
A>>3) Создать account, в котором balance = 0, MaxDecimal, MinDecimal и проверить что вызов GetBalance возвращает именно это значение. М>тоже не факт. балансы разные бывают. может, там транзакции пендятся. типа нам бабло зачисляют, но все никак не зачислят, то GetBalance его вернет. так же в тут же созданном акке может быть списане таньги за его создание (почему не может? очень даже может) или поклажа бонусов на счет.
Если мы создаем тестовый account, то мы же и контролируем его поведение. Тестирование на живой системе — это уже не unit tests.
М>вы уверены, что есть функция SetBalance? вот это точно бред. функция создать акк -- может быть. функция положить бабло -- тоже. функция списать бабло, вычислить проценты -- да. но прямой доступ к балансу на запись -- откуда он?
В реальном API скорее всего нет. Но для того чтобы продукт хорошо поддавался тестированию в нем или должены быть testability hooks (например какой-нибудь внутренний, доступный только в специальном private билде API) или мы будем использовать mock object. Если в реальной системе код работы с аккаунтом невозможно протестировать, то эта система smells и срочно требует к себе внимания.
A>>5) Вызывать GetBalance для последовательно увеличивающихся accountId и для последовательно уменьшающихся accountId. М>и что это даст?
Это уже включилось внутреннее чутье задницей, например что аккаунты хранятся в односвязном списке с дорогим доступом. И получение следующего элемента существенно отличается от получения предыдущего элемента. Здесь могут водиться баги. Ну и для самоуспокоения этим тестом (в дополнение к предыдущим) мы получаем полное покрытие операций выборки: заданный элемент, случайный элемент, первый элемент, последний элемент, следующий элемент и предыдущий элемент.
Вот еще пара идей:
6) Замерить среднее время (на большом количестве повторений) выполнения GetBalance. Проверить, что оно не превышает заданного порога.
7) Выполнить GetBalance в контексте разных пользователей. Возможно Bob имеет право доступа к данным, а Alice — нет.
Здравствуйте, abibok, Вы писали:
A>>>2) Дважды вызвать GetBalance, результаты должны совпадать. М>>не факт. пусть баланс функция от времени (почему нет? и такое бывает)
A>Мы не имеем никаких сведений о внутреннем устройстве функции, поэтому не будем усложнять. A>Считаем что за короткое время между двумя последовательными вызовами состояние системы не изменяется.
даже если в функции есть баг и функция возвращает мусор, то с одинаковыми аргументами и в одинаковом окружении мусор, скорее всего, будет один и тот же. и чтобы выловить баг между вызовами нужно откушать половину стека, забить его рандомными данными, освободить. тоже самое сделать с кучей...
A>>>3) Создать account, в котором balance = 0, MaxDecimal, MinDecimal и проверить что вызов GetBalance возвращает именно это значение. М>>тоже не факт. балансы разные бывают. может, там транзакции пендятся. типа нам бабло зачисляют, но все никак не зачислят, то GetBalance его вернет. так же в тут же созданном акке может быть списане таньги за его создание (почему не может? очень даже может) или поклажа бонусов на счет.
A>Если мы создаем тестовый account, то мы же и контролируем его поведение. Тестирование на живой системе — это уже не unit tests.
согласен, что контролируем, но не согласен, что мы знаем поведение функции по ее имени.
М>>вы уверены, что есть функция SetBalance? вот это точно бред. функция создать акк -- может быть. функция положить бабло -- тоже. функция списать бабло, вычислить проценты -- да. но прямой доступ к балансу на запись -- откуда он?
A>В реальном API скорее всего нет. Но для того чтобы продукт хорошо поддавался тестированию в нем или должены быть testability hooks (например какой-нибудь внутренний, доступный только в специальном private билде API) или мы будем использовать mock object. Если в реальной системе код работы с аккаунтом невозможно протестировать, то эта система smells и срочно требует к себе внимания.
согласен. юнит тест должен отрубить максимум посторонних функций. все эти бонусы и проценты. а вот тест на целостность как раз должен убедиться, что протестированные по отдельности компоненты работают в общей упряжке, но это как бы не юнит тест. а то ведь с такими рассуждениями можно и openURL предложить протестировать юнит тестом, которая "тянет" за собой целый браузер.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Здравствуйте, Кодёнок, Вы писали:
А>>Метод: А>>decimal GetBalance(int accountId) ??
Кё>Метод сам по себе тестировать нельзя — в любом случае будет создан экземпляр объекта.
Ну в некоторых языках есть так нелюбимые мной свободные функции. Поэтому дословно с Вами не соглашусь.
Но по смыслу очень согласен.
Для юнит тестирования нужно сначала определить границы этого самого юнита.
И это не всегда кстати возможно.
Вот представим, что внутри данной функции производится коннект к захардкоженой реальной продакшиновой базе и запрос туда.
Конечно ситуация почти нереальная но бывают очень похожие ситуации.
И повидав их я очень подозрительно выслушиваю заявления типа "Ведь просили же только прочесть из базы. Я сейчас за пол часа сделаю, а ты со своей архитектурой и тестами неделю будешь возиться!"
Кё>Этому объекту надо подсунуть базу-заглушку с несколькими известными аккаунтами и убедиться, что метод возвращает заранее известные значения, что возвращает то что надо для несуществующих ID, что не имеет побочных эффектов.
Один из вариантов.
Иногда даже удается протестировать отдельно бизнес логику и отдельно синхронизацию доменной модели и базы.
Так получается не всегда, но если получается то тесты очень быстро выполняются(а это иногда критично)
реализовать эти тесты тоже по-нормальному не получится, т.к. имплементации нет, моки не подложить и т.д.
Но в целом получится ответ по вопросу — предположения и сигнатуры тестов в ответ на недосказанность и сигнатуру метода. Клин клином, так сказать.
Зачем столько шума? Трудно продираться через все эти should, when, then, given. Народ прочитал, что так правильно и теперь натягивает этот паттерн на все тестовые методы. Так намного читабельнее:
VS>Зачем столько шума? Трудно продираться через все эти should, when, then, given. Народ прочитал, что так правильно и теперь натягивает этот паттерн на все тестовые методы. Так намного читабельнее: VS>
Не проверял, но уверен, что материалы по BDD несложно найти, и среди них немало будет начинаеться именно с "зачем". Там идея шире чем просто именование тестовых методов. Вкратце — should, потому что тест задаёт _требования_ к поведению системы или компонента. given/when/then — потому что требования выражаются в _структурированных примерах_.
Бесспорно, если вам/вашей команде так трудно, а по-другому легко, то у вас есть полное право BDD не применять.
VS>Кстати, из имен тестов совершенно непонятно, _когда_ он кидает исключения. При вызове любого метода? В конструкторе? В dispose? При сериализации?
Да, поскольку тестируется не метод, а требование к компоненту, реализующему определённую функцию. Соответственно упавший тест показывает не на метод, а на требование, которое компонент не обеспечивает.
P>Не проверял, но уверен, что материалы по BDD несложно найти, и среди них немало будет начинаеться именно с "зачем". Там идея шире чем просто именование тестовых методов. Вкратце — should, потому что тест задаёт _требования_ к поведению системы или компонента. given/when/then — потому что требования выражаются в _структурированных примерах_.
P>Бесспорно, если вам/вашей команде так трудно, а по-другому легко, то у вас есть полное право BDD не применять.
Спасибо. По-моему — такое соглашение — слишком формально и без видимых бенефитов. Строгое следование методологии хорошо на этапе изучения. И если получается дать имя тесту простой английской фразой, не по шаблону, — это замечательно.
P>Да, поскольку тестируется не метод, а требование к компоненту, реализующему определённую функцию. Соответственно упавший тест показывает не на метод, а на требование, которое компонент не обеспечивает.
Из _наименования_ тестового метода должно быть ясно, при каких условиях ожидается некое поведение. Из shouldThrowExceptionIfAccountClosed ясно только, что тестируемый компонент кидает исключение, если счет закрыт. Но вот в каком сценарии он это делает — не ясно. Может быть он висит себе в памяти и каждые 10 минут кидает исключение? Или он это делает, когда ему посылают некоторое подмножество сообщений? Чтобы это выяснить, надо смотреть код теста, что в разы увеличивает время, нужное на изучение компонента. Сгенерировать документацию по таким тестам невозможно — ценность ее будет минимальна.
VS>Из _наименования_ тестового метода должно быть ясно, при каких условиях ожидается некое поведение. Из shouldThrowExceptionIfAccountClosed ясно только, что тестируемый компонент кидает исключение, если счет закрыт. Но вот в каком сценарии он это делает — не ясно. Может быть он висит себе в памяти и каждые 10 минут кидает исключение? Или он это делает, когда ему посылают некоторое подмножество сообщений? Чтобы это выяснить, надо смотреть код теста, что в разы увеличивает время, нужное на изучение компонента. Сгенерировать документацию по таким тестам невозможно — ценность ее будет минимальна.
Если вы следуете SRP в дизайне, то запутаться будет сложно. В этом конкретном случае всё ещё проще — входные условия таковы, что во всей картине мира существует один единственный метод. Но в целом я не спорю, понятные названия для тестов важны и приветствуются.
Не могу комментировать подход с генерированием документации по тестам — не сталкивался. Могу сказать что при хорошем DSL тесты неплохо читаются в исходном виде.
Ну и пожалуй, повторюсь — если у вас есть своё видение того, как что должно быть, и оно для вас работает — пусть работает и дальше.
Если же такового нет — как у топикстартера — то вполне можно посмотреть на BDD, как продуманный, описанный, и практичный подход, продолжающий идею TDD.