Я пишу парсер, и как часть этого, у меня есть Expander
класс, который «расширяет» одно сложное утверждение в несколько простых операторов. Например, это расширило бы это:
x = 2 + 3 * a
в:
tmp1 = 3 * a
x = 2 + tmp1
Сейчас я думаю о том, как тестировать этот класс, в частности, как организовать тесты. Я мог бы вручную создать дерево синтаксиса ввода:
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
Или я мог бы написать это как строку и разобрать это:
var input = new Parser().ParseStatement("x = 2 + 3 * a");
Второй вариант гораздо проще, короче и удобочитаемее. Но он также вводит зависимость от Parser
, что означает, что ошибка Parser
может не пройти этот тест. Таким образом, тест перестанет быть модульным тестом Expander
, и я думаю, что технически станет интеграционным тестом Parser
и Expander
.
Мой вопрос: можно ли в основном (или полностью) полагаться на такого рода интеграционные тесты для тестирования этого Expander
класса?
Parser
может провалиться в каком-то другом тесте, не является проблемой, если вы обычно делаете коммит только при нулевых сбоях, напротив, это означает, что у вас больше охватаParser
. То, о чем я бы предпочел беспокоиться, это то, что ошибкаParser
могла сделать этот тест успешным, если он не прошел . В конце концов, есть модульные тесты, чтобы найти ошибки - тест не работает, но он должен быть.Ответы:
Вы обнаружите, что пишете намного больше тестов, гораздо более сложного, интересного и полезного поведения, если вы можете сделать это просто. Так что вариант, который включает в себя
вполне допустимо. Это зависит от другого компонента. Но все зависит от десятков других компонентов. Если вы издеваетесь над чем-то с точностью до дюйма, вы, вероятно, зависите от множества насмешливых функций и тестовых приспособлений.
Разработчики иногда чрезмерно сосредоточиться на чистоте своих модульных тестов , или разработка модульных тестов и модульных тестов только , без какого - либо модуля, интеграции, стресса или других видов испытаний. Все эти формы являются действительными и полезными, и все они несут надлежащую ответственность разработчиков, а не только Q / A или оперативный персонал в дальнейшем.
Один из подходов, который я использовал, состоит в том, чтобы начать с этих прогонов более высокого уровня, а затем использовать полученные из них данные для построения длинного выражения теста с наименьшим общим знаменателем. Например, когда вы выгружаете структуру данных из
input
созданного выше, вы можете легко построить:вид теста, который тестирует на самом низком уровне. Таким образом, вы получите хороший микс: несколько самых простых, примитивных тестов (чистые юнит-тесты), но не потратили недели на написание тестов на этом примитивном уровне. Это дает вам время, необходимое для написания гораздо большего количества чуть менее атомарных тестов с
Parser
использованием помощника. Конечный результат: больше тестов, больше охвата, больше углов и других интересных случаев, лучший код и более высокая гарантия качества.источник
Конечно, все в порядке!
Вам всегда нужен функциональный / интеграционный тест, который выполняет полный путь кода. И полный путь к коду в этом случае означает в том числе оценку сгенерированного кода. То есть вы проверить , что синтаксический
x = 2 + 3 * a
производит код, если запустить сa = 5
установитx
на17
и при запуске сa = -2
задастx
в-4
.Ниже вы должны выполнить модульные тесты для меньших битов, если это действительно помогает отлаживать код . Чем более детализированы тесты, тем выше вероятность того, что любое изменение в коде также должно будет изменить тест, поскольку изменяется внутренний интерфейс. Такие испытания имеют небольшую долговременную ценность и увеличивают объем работ по техническому обслуживанию. Так что есть точка убывающей отдачи, и вы должны остановиться до этого.
источник
Модульные тесты позволяют вам определить конкретные элементы, которые ломаются и где в коде они сломались. Так что они хороши для очень мелкозернистого тестирования. Хорошие юнит-тесты помогут сократить время отладки.
Однако из моего опыта юнит-тесты редко бывают достаточно хорошими, чтобы действительно проверить правильность работы. Поэтому интеграционные тесты также полезны для проверки цепочки или последовательности операций. Интеграционные тесты помогут вам пройти функциональное тестирование. Однако, как вы указали, из-за сложности интеграционных тестов труднее найти конкретное место в коде, где тесты прерываются. Он также обладает несколько большей хрупкостью, поскольку сбои в любой части цепочки могут привести к провалу теста. Однако в производственном коде эта цепочка все еще будет присутствовать, поэтому ее проверка по-прежнему полезна.
В идеале у вас было бы и то и другое, но, во всяком случае, лучше иметь автоматический тест, чем не иметь тест.
источник
Выполните много тестов на анализаторе, и, когда анализатор пройдет тесты, сохраните эти выходные данные в файл, чтобы смоделировать анализатор и протестировать другой компонент.
источник