Почему тестирование языка не поддерживается на уровне синтаксиса?


37

Вы можете найти бесконечный список блогов, статей и сайтов, рекламирующих преимущества модульного тестирования вашего исходного кода. Почти гарантировано, что разработчики, которые программировали компиляторы для Java, C ++, C # и других типизированных языков, использовали модульное тестирование для проверки своей работы.

Так почему же, несмотря на свою популярность, тестирование отсутствует в синтаксисе этих языков?

Microsoft представила LINQ для C # , так почему же они не могли добавить тестирование?

Я не пытаюсь предсказать, какими будут эти языковые изменения, но чтобы прояснить, почему они отсутствуют с самого начала.

В качестве примера: мы знаем, что вы можете написать forцикл без синтаксиса forоператора. Вы можете использовать whileили if/ gotoзаявления. Кто-то решил, что forзаявление было более эффективным, и ввел его в язык.

Почему тестирование не следовало той же эволюции языков программирования?


24
Действительно ли лучше сделать спецификацию языка более сложной, добавив синтаксис, в отличие от разработки языка с простыми правилами, которые можно легко расширить универсальным способом?
KChaloux

6
Что вы подразумеваете под "на уровне синтаксиса"? У вас есть какая-нибудь деталь / идея о том, как это будет реализовано?
SJuan76

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

12
@FrustratedWithFormsDesigner См языка D для примера.
Довал

4
Еще один язык со встроенными функциями модульного тестирования - это Rust.
Себастьян Редл

Ответы:


36

Как и во многих других случаях, модульное тестирование лучше всего поддерживается на уровне библиотеки, а не на уровне языка. В частности, в C # имеется множество библиотек модульного тестирования, а также вещи, которые являются родными для .NET Framework, например Microsoft.VisualStudio.TestTools.UnitTesting.

Каждая библиотека модульного тестирования имеет несколько разные принципы и синтаксис тестирования. При прочих равных условиях, лучший выбор лучше, чем меньше. Если бы модульное тестирование было запрограммировано на язык, вы либо были бы привязаны к выбору дизайнера языка, либо использовали бы ... библиотеку, и вообще отказались бы от возможностей языкового тестирования.

Примеры

  • Nunit - универсальная идиоматически разработанная инфраструктура для модульного тестирования, которая в полной мере использует возможности языка C #.

  • Moq - Mocking Framework, который в полной мере использует лямбда-выражения и деревья выражений без метафоры записи / воспроизведения.

Есть много других вариантов. Такие библиотеки, как Microsoft Fakes, могут создавать макеты "shims ...", которые не требуют написания ваших классов с использованием интерфейсов или виртуальных методов.

Linq не является языковой функцией (несмотря на название)

Linq - это библиотека. Мы получили много новых функций в самом языке C # бесплатно, таких как лямбда-выражения и методы расширения, но фактическая реализация Linq находится в .NET Framework.

Есть некоторый синтаксический сахар, который был добавлен в C #, чтобы сделать операторы linq более чистыми, но этот сахар не требуется для использования linq.


1
Я согласен с тем, что LINQ не является языковой функцией, но для ее реализации необходимы некоторые базовые языковые функции - в частности, дженерики и динамические типы.
Уайетт Барнетт

@WyattBarnett: Да, то же самое относится и к простому модульному тестированию - например, к отражению и атрибутам.
Док Браун

8
LINQ нужны лямбды и деревья выражений. Обобщения уже были в C # (и .Net в целом, они присутствуют в CLR), а динамика не имеет ничего общего с LINQ; Вы можете сделать LINQ без динамики.
Артур ван Леувен

DBIx :: Class в Perl является примером LINQ-подобной функциональности, реализованной более 10 лет спустя после того, как все функции, необходимые для ее реализации, появились в языке.
Slebetman

2
@ Carson63000 Я думаю, что вы имеете в виду анонимные типы в сочетании с varключевым словом :)
M.Mimpen

21

Есть много причин. Эрик Липперт много раз заявлял, что причина feature Xне в C #, а в том, что его нет в бюджете. Языковые дизайнеры не имеют бесконечного количества времени и денег для реализации, и каждая новая функция связана с затратами на обслуживание. Сохранение языка как можно меньшего размера не просто легче для разработчиков языка, но и для любого, кто пишет альтернативные реализации и инструменты (например, IDE). Кроме того, когда что-то реализовано с точки зрения языка, а не его части, вы получаете Портативность бесплатно. Если модульное тестирование реализуется как библиотека, вам нужно написать ее только один раз, и она будет работать в любой соответствующей реализации языка.

Стоит отметить, что D имеет поддержку на уровне синтаксиса для модульного тестирования . Я не знаю, почему они решили добавить это, но стоит отметить, что D предназначен для «языка системного программирования высокого уровня». Дизайнеры хотели, чтобы он был жизнеспособным для небезопасного низкоуровневого кода, для которого традиционно используется C ++, а ошибка в небезопасном коде невероятно дорогая - неопределенное поведение. Поэтому я полагаю, что им было бы целесообразно приложить дополнительные усилия ко всему, что поможет вам проверить, работает ли какой-нибудь небезопасный код. Например, вы можете принудить, чтобы только определенные доверенные модули могли выполнять небезопасные операции, такие как непроверенный доступ к массиву или арифметика указателя.

Быстрая разработка также была для них приоритетом, настолько, что они поставили перед собой цель разработки, чтобы D-код компилировался достаточно быстро, чтобы его можно было использовать в качестве языка сценариев. Выпекание юнит-тестов прямо на языке, так что вы можете запустить свои тесты, просто передав дополнительный флаг компилятору, это поможет.

Тем не менее, я думаю, что отличная библиотека модульного тестирования делает гораздо больше, чем просто находит некоторые методы и запускает их. Взять, к примеру, QuickCheck на Haskell , который позволяет вам проверять такие вещи, как «для всех x и y f (x, y) == f (y, x)». QuickCheck лучше описать как генератор модульных тестов и позволяет вам тестировать вещи на более высоком уровне, чем «для этого входа, я ожидаю этот вывод». QuickCheck и Linq не так уж сильно отличаются - они оба являются предметно-ориентированными языками. Так что вместо того, чтобы связывать поддержку модульного тестирования с языком, почему бы не добавить функции, необходимые для практической реализации DSL? В результате вы получите не только модульное тестирование, но и лучший язык.


3
Чтобы остаться в мире .Net, есть FsCheck, который является портом QuickCheck для F #, с некоторыми помощниками для использования из C #.
Артур ван Леувен

Чтобы остаться в мире .NET? Этот вопрос не имеет ничего общего с .NET.
Майлз Рут

@MilesRout C # является частью .NET. Вопрос и ответ часто говорят о C #, и вопрос даже говорит о LINQ.
Захари Доу

Вопрос не помечен .NET или C #.
Майлз Рут

@MilesRout Это может быть правдой, но вы не можете обоснованно сказать , что это не имеет ничего общего с ним только потому , что он не имеет тег. :)
Захари Доу

13

Потому что тестирование и, в частности, разработка, основанная на тестировании, - явление глубоко противоречивое.

Почти каждый программист начинает свою карьеру, полагая, что он гораздо лучше справляется со сложностью, чем на самом деле. Тот факт, что даже величайший программист не может писать большие и сложные программы без серьезных ошибок, если они не используют множество регрессионных тестов, серьезно разочаровывает и даже позорит многих практиков. Отсюда превалирующее нежелание регулярно проходить тестирование даже среди профессионалов, которые уже должны знать лучше.

Я думаю, что тот факт, что религиозное тестирование постепенно становится все более распространенным и ожидаемым, во многом связан с тем фактом, что с ростом емкости хранилища и вычислительной мощности создаются более крупные системы, чем когда-либо, а чрезвычайно большие системы особенно подвержены коллапсу сложности, что не может управляться без защитной сетки регрессионных тестов. В результате, даже особенно упрямые и заблуждающиеся разработчики теперь неохотно признают, что им нужно тестирование и что они всегда будут нуждаться в тестировании (если это звучит как признание на собрании АА, это вполне преднамеренно - для многих психологически трудно допустить необходимость сети безопасности) физические лица).

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


3
Вы немного преувеличиваете свое дело. В то время как модульное тестирование, несомненно, имеет первостепенное значение, правильное построение программ , способ минимизировать ненужную сложность, одинаково важен, если не более того. Такие языки, как Haskell, имеют системы типов, которые повышают надежность и доказуемость написанных на них программ, а лучшие практики в кодировании и проектировании позволяют избежать многих ловушек непроверенного кода, делая модульное тестирование менее критичным, чем это было бы в противном случае.
Роберт Харви

1
@RobertHarve ... и модульное тестирование - очень хороший способ косвенно подтолкнуть разработчиков к этому. Работа над API при создании тестов, а не при использовании его с другим кодом, помогает правильно их расположить, чтобы ограничить или удалить неплотные абстракции или нечетные вызовы методов. Я не думаю, что KillianFoth преувеличивает что-либо.
Изката

@ Роберт: Единственное, что я полагаю, что он преувеличивает, что «религиозное тестирование постепенно становится все более распространенным». Если медленно определить его в терминах географических временных рамок, он, вероятно, не за горами ..... :)
mattnz

+1 В моей нынешней работе я поражен приливным сдвигом в отношении развития с новыми библиотеками и доступными технологиями. Впервые в моей карьере эти ребята учат меня использовать более сложный процесс разработки, вместо того, чтобы чувствовать, что я на вершине холма, кричащего на ветер. Я согласен, что это просто вопрос времени, когда эти отношения найдут выражение в синтаксисе родного языка.
Роб

1
Извините, еще одна мысль: комментарий Роберта Харви. В настоящее время системы типов являются хорошей проверкой, но на самом деле они не отвечают определению ограничений типов - они обращаются к интерфейсу, но не ограничивают правильное поведение. (т.е. 1 + 1 == 3 скомпилируется, но может или не может быть верным, в зависимости от реализации +) Интересно, будет ли следующая тенденция видеть более выразительные системы типов, которые сочетают в себе то, что мы сейчас считаем модульным тестированием.
Роб

6

Многие языки поддерживают тестирование. С утверждает, что тесты могут провалиться. На этом большинство языков останавливаются, но в Eiffel и совсем недавно в Ada 2012 есть преинварианты (вещи, которые должны быть переданы аргументам функции) и postinvariants (вещи, которые должен передавать результат функции), при этом Ada предлагает возможность ссылаться на начальные аргументы в postinvariant. Ада 2012 также предлагает инварианты типа, поэтому всякий раз, когда метод вызывается для класса Ада, инвариант типа проверяется перед возвратом.

Это не тот тип полного тестирования, который может дать хорошая инфраструктура тестирования, но это важный тип тестирования, который языки могут лучше всего поддерживать.


2
Проделав небольшую работу в Eiffel и будучи обширным пользователем утверждений, мой опыт показывает, что все, что вы можете сделать, что является контрактным по своей природе, помогает выявить множество ошибок.
Blrfl

2

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

Два, имхо, хороший пример этого из F # для Fun и Profit здесь и здесь

Лично я до сих пор верю в ценность модульных тестов, но есть некоторые веские аргументы. Например, если недопустимое состояние в коде непредставимо, тогда не только нет необходимости писать модульные тесты для этого случая, это невозможно.


2

Я бы сказал, что вы пропустили добавление некоторых необходимых функций, потому что они не были выделены, как для модульного тестирования .

Например, модульное тестирование в C # в основном осуществляется с помощью атрибутов. Функция пользовательских атрибутов предоставляет богатый механизм расширения, который позволяет таким структурам, как NUnit, выполнять итерации и конкурировать с такими вещами, как теоретическое и параметризованное тестирование.

Это подводит меня ко второму важному моменту - мы недостаточно знаем о том, что делает тестируемость хорошей, чтобы превратить ее в язык. Темпы инноваций в тестировании намного быстрее, чем у других языковых конструкций, поэтому нам нужно иметь гибкие механизмы в наших языках, чтобы оставить свободу для инноваций.

Я пользователь, но не фанат TDD - в некоторых случаях он очень полезен, особенно для улучшения вашего дизайнерского мышления. Это не обязательно полезно для более крупных унаследованных систем - высокоуровневое тестирование с хорошими данными и автоматизацией, вероятно, принесет больше пользы наряду с сильной культурой проверки кода .

Другие соответствующие чтения:

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.