о юнит-тестах (программ.)
Oct. 9th, 2007 10:18 pmЯ отношусь с некоторым подозрением к созданию юнит-тестов, покрывающих весь объем исходного кода, в таких языках, как C++ и Java.
В целом мне нравится идея TDD (test-driven development), и мне не раз случалось убеждаться в пользе большого числа быстрых тестов. Но вместе с тем, у меня есть несколько возражений, особенно в случае C++/Java.
Во-первых, если я правильно понимаю историю (тут я повторяю то, что читал где-то), движение TDD зародилось в среде динамических языков. В языках с статическими проверками типизации значительная часть того, что проверяют юнит-тесты в динамических языках, проверяется "бесплатно" компилятором (естественно, это не значит, что динамические языки хуже - у динамической типизации есть свои преимущества). Это, казалось бы, хорошо, но на практике нередко оказывается, что юнит-тесты на C++/Java выходят почти тривиальными, за счет того, что из типичного для динамического языка юнит-теста убирают проверки типизации. В принципе эта проблема решается, нужно просто научиться писать более подходящие для этих языков юнит-тесты, в которых достаточно "мяса".
Во-вторых, слишком много ресурсов и времени уходит на подготовку кода к тому, чтобы его оттестировать (как правило, значительно больше, чем в динамических языках, потому что в них вставлять и втискиваться внутрь существующего кода обычно намного легче). Всевозможные mock'и, разные схемы dependency injection, и прочая мишура, нужная для того, чтобы в конце концов гордо запустить юнит-тест, изолирующий сложный класс, симулирующий с помощью разного рода методов все его заимодействие с внешней средой, и в итоге почти тривиализирующий его поведение, так что тест проходит и все замечательно, да? - нет, потом окажется, что баги мигрировали в область интеракции класса с его окружением.
Я не согласен с довольно распостраненной точкой зрения, гласящей, что вся эта мишура - mock'и, дополнительные интерфейсы/абстрактные классы, позволяющие тестам легко втиснуться в иерархию, и проч. и проч. - что все это полезно само по себе, а не только потому, что помогает тестировать. Что дескать это очень правильно, мы упрощаем и абстрагируем код для удобства проверок, и он от этого только улучшается, поэтому время, на это потраченное, себя окупает. Мне это кажется довольно малооправданным примером wishful thinking. Иногда так действительно бывает; но очень часто бывает и так, что навороченные внутри и снаружи кода бутафорные пристройки на самом деле не проясняют код, не улучшают его, и ни для чего, кроме как запуска тестов, не пригодятся. Я такое видел много раз. Можно провести определенную параллель с design pattern'ами. На них тоже у многих людей едут мозги, и они начинают творить кучи паттернов там, где можно было сделать многое проще, и гордиться самим фактом использования паттернов.
Вместе с тем я все же, несмотря на эти подозрения, принимаю практику массивного юнит-тестирования кода на C++ и Java. Преимущества - обычно - перевешивают вышеупомянутые недостатки. Просто я считаю, что не стоит доходить в этом до фанатизма (и это не пустое заявление - примеры такого фанатизма, по крайней мере по моему мнению, я нередко вижу вокруг себя), и стоит понимать, что цена, которую мы платим за адаптацию нашего кода в C++/Java к максимальному юнит-тестированию, часто бывает весьма высокой, выше, чем кажется на первый взгляд.
В целом мне нравится идея TDD (test-driven development), и мне не раз случалось убеждаться в пользе большого числа быстрых тестов. Но вместе с тем, у меня есть несколько возражений, особенно в случае C++/Java.
Во-первых, если я правильно понимаю историю (тут я повторяю то, что читал где-то), движение TDD зародилось в среде динамических языков. В языках с статическими проверками типизации значительная часть того, что проверяют юнит-тесты в динамических языках, проверяется "бесплатно" компилятором (естественно, это не значит, что динамические языки хуже - у динамической типизации есть свои преимущества). Это, казалось бы, хорошо, но на практике нередко оказывается, что юнит-тесты на C++/Java выходят почти тривиальными, за счет того, что из типичного для динамического языка юнит-теста убирают проверки типизации. В принципе эта проблема решается, нужно просто научиться писать более подходящие для этих языков юнит-тесты, в которых достаточно "мяса".
Во-вторых, слишком много ресурсов и времени уходит на подготовку кода к тому, чтобы его оттестировать (как правило, значительно больше, чем в динамических языках, потому что в них вставлять и втискиваться внутрь существующего кода обычно намного легче). Всевозможные mock'и, разные схемы dependency injection, и прочая мишура, нужная для того, чтобы в конце концов гордо запустить юнит-тест, изолирующий сложный класс, симулирующий с помощью разного рода методов все его заимодействие с внешней средой, и в итоге почти тривиализирующий его поведение, так что тест проходит и все замечательно, да? - нет, потом окажется, что баги мигрировали в область интеракции класса с его окружением.
Я не согласен с довольно распостраненной точкой зрения, гласящей, что вся эта мишура - mock'и, дополнительные интерфейсы/абстрактные классы, позволяющие тестам легко втиснуться в иерархию, и проч. и проч. - что все это полезно само по себе, а не только потому, что помогает тестировать. Что дескать это очень правильно, мы упрощаем и абстрагируем код для удобства проверок, и он от этого только улучшается, поэтому время, на это потраченное, себя окупает. Мне это кажется довольно малооправданным примером wishful thinking. Иногда так действительно бывает; но очень часто бывает и так, что навороченные внутри и снаружи кода бутафорные пристройки на самом деле не проясняют код, не улучшают его, и ни для чего, кроме как запуска тестов, не пригодятся. Я такое видел много раз. Можно провести определенную параллель с design pattern'ами. На них тоже у многих людей едут мозги, и они начинают творить кучи паттернов там, где можно было сделать многое проще, и гордиться самим фактом использования паттернов.
Вместе с тем я все же, несмотря на эти подозрения, принимаю практику массивного юнит-тестирования кода на C++ и Java. Преимущества - обычно - перевешивают вышеупомянутые недостатки. Просто я считаю, что не стоит доходить в этом до фанатизма (и это не пустое заявление - примеры такого фанатизма, по крайней мере по моему мнению, я нередко вижу вокруг себя), и стоит понимать, что цена, которую мы платим за адаптацию нашего кода в C++/Java к максимальному юнит-тестированию, часто бывает весьма высокой, выше, чем кажется на первый взгляд.
no subject
Date: 2007-10-10 07:16 pm (UTC)