Информация об изменениях

Сообщение дебагинг vs unit-тесты от 29.04.2016 13:33

Изменено 29.04.2016 13:43 _hum_

Всегда считал, что это ортогональные вещи, но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:

L>Вот смотри. Сейчас ваш процесс выглядит примерно так.


L>Тимлид: Нужно [исправить баг А|реализовать стори B|быстро что-то подправить в модуле C]

L>Разраб: Yes, sir!

L>Разработчк изменяет пару строк кода где-то очень глубоко в кишках исходников. И говорит "готово!". А тимлид "А ты проверил?". И разработчик идет курить бамбук, пока все пересобирается, чтобы потом еще полчаса вручную докликивать программу в состояние, в котором он может косвенно проверить часть функциональности внесенного изменения. Обнаруживает баг в багфиксе и все по-новому. Два часа рабочего времени жестоко убито. Мотивация утопилась в унитазе, а профессиональная гордость хлещет водяру из горла.


L>А вот как выглядит процесс разработки здорового человека:

  текст
L>Разраб: так, что у нас тут в беклоге вниманием обделено? О! Поддержка формата времени протокола (страшная аббривеатура) в сетевом модуле.
L>Разраб: Ага, понятно, наконец-то мы будет показывать настоящее время, а не забивать его <unknown>. Так, где у нас спецификация...
L>(5 минут)
L>Разраб: Я хочу того же, что курили аффтфры спецификации протокола. Открывает Wireshark и выдергивает из сетевой сессии с прибором сообщения, содержащие оные типы. В случае отсутствия прибора или wireshark'a изобретает Hex-dump нужного PDU самостоятельно или заимствует его из спецификации, если аффтары озаботились примерами. В процессе придумывает забавные примеры невалидного элемента. Добавляет в протокола поддержку нового типа, а в проект юнит-тестов — проверку работы парсера, используюя награбленные или придуманные сырые данные. Не забывает тест, в котором на вход парсера подается мусор или специально инвалидированные данные. Щелкает "билд" на юнит-тест проекте. Оный собирает измененную либу парсера протокола, собирается сам и запускается. В консоли — 2 failed tests, которые разработчик нарочно зафейлил, чтобы проверить сам себя. Исправляет проваленные тесты, добавляет новые для граничных условий и обнаруженных серых пятен в спецификации, перезапускает билд. Через 5 минут — XXX tests passed.

L>В итоге — полностью реализовананя новая функциональность, покрытая тестами и даже в некоторых случаях прошедшая regression — ранее написанные тесты покажут, если новая функциональность вносит breaking change. Причем, за время, которого не хватило бы на полную сборку проекта и хотя бы один ручной тестовый прогон. Причем, проект автоматически прогоняет даже такие условия, которые во время ручного теста проверить невозможно.


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

Потому вопрос в зал, может кто-нибудь, кто придерживается мнения landerhigh, на примере задачи написания функции транспонирования матрицы показать, как тесты заменяют работу с дебагером?
дебагинг vs unit-тесты
Всегда считал, что это ортогональные вещи, но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:

L>Вот смотри. Сейчас ваш процесс выглядит примерно так.


L>Тимлид: Нужно [исправить баг А|реализовать стори B|быстро что-то подправить в модуле C]

L>Разраб: Yes, sir!

L>Разработчк изменяет пару строк кода где-то очень глубоко в кишках исходников. И говорит "готово!". А тимлид "А ты проверил?". И разработчик идет курить бамбук, пока все пересобирается, чтобы потом еще полчаса вручную докликивать программу в состояние, в котором он может косвенно проверить часть функциональности внесенного изменения. Обнаруживает баг в багфиксе и все по-новому. Два часа рабочего времени жестоко убито. Мотивация утопилась в унитазе, а профессиональная гордость хлещет водяру из горла.


L>А вот как выглядит процесс разработки здорового человека:

  текст
L>Разраб: так, что у нас тут в беклоге вниманием обделено? О! Поддержка формата времени протокола (страшная аббривеатура) в сетевом модуле.
L>Разраб: Ага, понятно, наконец-то мы будет показывать настоящее время, а не забивать его <unknown>. Так, где у нас спецификация...
L>(5 минут)
L>Разраб: Я хочу того же, что курили аффтфры спецификации протокола. Открывает Wireshark и выдергивает из сетевой сессии с прибором сообщения, содержащие оные типы. В случае отсутствия прибора или wireshark'a изобретает Hex-dump нужного PDU самостоятельно или заимствует его из спецификации, если аффтары озаботились примерами. В процессе придумывает забавные примеры невалидного элемента. Добавляет в протокола поддержку нового типа, а в проект юнит-тестов — проверку работы парсера, используюя награбленные или придуманные сырые данные. Не забывает тест, в котором на вход парсера подается мусор или специально инвалидированные данные. Щелкает "билд" на юнит-тест проекте. Оный собирает измененную либу парсера протокола, собирается сам и запускается. В консоли — 2 failed tests, которые разработчик нарочно зафейлил, чтобы проверить сам себя. Исправляет проваленные тесты, добавляет новые для граничных условий и обнаруженных серых пятен в спецификации, перезапускает билд. Через 5 минут — XXX tests passed.

L>В итоге — полностью реализовананя новая функциональность, покрытая тестами и даже в некоторых случаях прошедшая regression — ранее написанные тесты покажут, если новая функциональность вносит breaking change. Причем, за время, которого не хватило бы на полную сборку проекта и хотя бы один ручной тестовый прогон. Причем, проект автоматически прогоняет даже такие условия, которые во время ручного теста проверить невозможно.


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

Потому вопрос в зал, может кто-нибудь, кто придерживается мнения landerhigh, на примере задачи написания функции транспонирования матрицы показать, как тесты заменяют работу с дебагером?



UPD.

L>Вот сходил ты в курилку и придумал, что назовешь свою функцию

L>
L>    Matrix transpose(const Matrix& original);
L>


L>Налил кофе и написал следующие три теста

L>
L>    TEST(transposeMatrix, transposeNegative)
L>    {
L>        // this is more a border case test
L>        ASSERT_TRUE(transpose(emptyMatrix).empty());   // Also checks that we don't crash if matrix is empty
L>    }

L>    TEST(transposeMatrix, transposePositiveSymmetrical)
L>    {
L>        Matrix symmetricalMatrix = ...; // Initialize
L>        Matrix expected = ...; // Manually transposed matrix
L>        ASSERT_EQ(expected, transpose(symmetricalMatrix));
L>        ASSERT_EQ(symmetricalMatrix, transpose(transpose(symmetricalMatrix)));
L>    }

L>    TEST(transposeMatrix, transposePositiveAsymmetrical)
L>    {
L>        Matrix asymmetricalMatrix = ...; // Initialize
L>        Matrix expected = ...; // Manually transposed matrix
L>        ASSERT_EQ(expected, transpose(asymmetricalMatrix ));
L>        ASSERT_EQ(asymmetricalMatrix , transpose(transpose(asymmetricalMatrix )));
L>    }
L>


L>Потом написал собственно код транспонирования.


L>Запустил тест. На все-про-все 5 минут времени и у тебя гораздо более полное покрытие, нежели ты может добиться ручными проверками в отладчике.



L>Ты мне лучше скажи, зачем тут вообще отладчик может понадобиться?


ответ:
так а где здесь код-то самого транспонирования? ведь ошибки именно там кроются. и если вы увидите срабатывание ассертов, что делать дальше будете?
как, например, обнаружите глупейшую ошибку типа Matrix[i][j] = Matrix[j][i] для in-place трансполнирования квадратной матрицы?