При создании системы, подобной ИИ, которая может очень быстро идти по множеству различных путей, или по сути любому алгоритму, имеющему несколько разных входных данных, возможный набор результатов может содержать большое количество перестановок.
Какой подход следует использовать, чтобы использовать TDD при создании системы, которая выдает много-много разных комбинаций результатов?
Ответы:
Более практичный подход к ответу pdr . TDD - это скорее дизайн программного обеспечения, а не тестирование. Вы используете модульные тесты для проверки своей работы.
Таким образом, на уровне юнит-теста вам нужно спроектировать блоки так, чтобы они могли быть протестированы полностью детерминированным способом. Вы можете сделать это, взяв все, что делает единицу недетерминированной (например, генератор случайных чисел) и абстрагировав это. Скажем, у нас есть наивный пример того, как метод решает, хорош ли ход или нет:
Этот метод очень сложно протестировать, и единственное, что вы действительно можете проверить в модульных тестах, это его границы ... но для этого нужно много попыток. Поэтому вместо этого давайте абстрагируем случайную часть, создав интерфейс и конкретный класс, который оборачивает функциональность:
Теперь
Decider
класс должен использовать конкретный класс через его абстракцию, то есть интерфейс. Такой способ называется внедрением зависимостей (пример ниже является примером внедрения конструктора, но вы также можете сделать это с помощью установщика):Вы можете спросить себя, зачем нужен этот «кодовый блат». Ну, во-первых, теперь вы можете посмеяться над поведением случайной части алгоритма, потому что
Decider
теперь есть зависимость, которая следует заIRandom
«контрактом». Вы можете использовать для этого фальшивый фреймворк, но этот пример достаточно прост для написания кода:Самое приятное то, что это может полностью заменить «фактическую» конкретную реализацию. Код становится легко тестировать следующим образом:
Надеюсь, что это даст вам идеи о том, как спроектировать ваше приложение, чтобы перестановки можно было форсировать, чтобы вы могли протестировать все крайние случаи и еще много чего.
источник
Строгий TDD имеет тенденцию немного ломаться для более сложных систем, но это не имеет большого значения с практической точки зрения - как только вы выйдете за пределы возможности изолировать отдельные входы, просто выберите несколько тестовых случаев, которые обеспечивают разумное покрытие, и используйте их.
Это требует определенных знаний о том, что будет делать реализация, но это скорее теоретическая проблема - вы вряд ли создадите ИИ, который был детально указан нетехническими пользователями. Он относится к той же категории, что и тестирование путем жесткого кодирования к тестовым примерам - официально тест является спецификацией, и реализация является одновременно правильным и самым быстрым из возможных решений, но на самом деле этого никогда не происходит.
источник
TDD не о тестировании, а о дизайне.
Далеко не разваливающийся со сложностью, он превосходен в этих обстоятельствах. Это заставит вас рассмотреть более крупную проблему в более мелких частях, что приведет к лучшему дизайну.
Не пытайтесь проверить каждую перестановку вашего алгоритма. Просто создайте тест за тестом, напишите самый простой код, чтобы тест работал, пока вы не разберетесь со своими основами. Вы должны понять, что я имею в виду, чтобы устранить проблему, потому что вам будет предложено подделать части проблемы во время тестирования других частей, чтобы избавить себя от необходимости писать 10 миллиардов тестов для 10 миллиардов перестановок.
Изменить: я хотел добавить пример, но не было времени раньше.
Давайте рассмотрим алгоритм сортировки по месту. Мы можем пойти дальше и написать тесты, которые охватывают верхний конец массива, нижний конец массива и всевозможные странные комбинации в середине. Для каждого из нас мы должны были бы построить полный массив какого-либо объекта. Это займет время.
Или мы могли бы решить проблему в четырех частях:
Первая - единственная сложная часть проблемы, но, абстрагируя ее от остальных, вы сделали ее намного проще.
Второй почти наверняка обрабатывается самим объектом, по крайней мере, необязательно, во многих статических типах фреймворков будет интерфейс, показывающий, реализована ли эта функциональность. Так что вам не нужно проверять это.
Третий невероятно легко проверить.
Четвертый просто обрабатывает два указателя, просит класс обхода переместить указатели, вызывает сравнение и, в зависимости от результата этого сравнения, требует замены элементов. Если вы подделали первые три проблемы, вы можете проверить это очень легко.
Как мы привели к улучшению дизайна здесь? Допустим, вы сохранили простоту и реализовали пузырьковую сортировку. Это работает, но когда вы отправляетесь в производство и обрабатываете миллион объектов, это происходит слишком медленно. Все, что вам нужно сделать, это написать новую функцию обхода и поменять ее местами. Вам не придется сталкиваться со сложностью обработки трех других проблем.
В этом разница между модульным тестированием и TDD. Юнит-тестер скажет, что это сделало ваши тесты хрупкими, что если бы вы тестировали простые входные и выходные данные, вам теперь не пришлось бы писать больше тестов для вашей новой функциональности. TDDer скажет, что я разделил проблемы соответствующим образом, так что каждый мой класс хорошо выполняет одно и одно.
источник
Невозможно проверить каждую перестановку вычислений со многими переменными. Но в этом нет ничего нового, это всегда было верно для любой программы выше сложности игрушки. Смысл тестов заключается в проверке свойства вычислений. Например, сортировка списка с 1000 номерами требует некоторых усилий, но любое индивидуальное решение может быть проверено очень легко. Теперь хотя их 1000! возможные (классы) входные данные для этой программы, и вы не можете проверить их все, вполне достаточно просто случайным образом сгенерировать 1000 входных данных и убедиться, что выходные данные действительно отсортированы. Почему? Потому что почти невозможно написать программу, которая надежно сортирует 1000 случайно сгенерированных векторов, не будучи в целом корректной (если вы не намеренно настраиваете ее для манипулирования определенными магическими данными ...)
Теперь, в общем, все немного сложнее. Там действительно есть были ошибки , когда почтовая программа не будет доставлять сообщения электронной почты для пользователей , если они имеют «F» в их имени пользователя и день недели пятница. Но я считаю, что это попытка предвидеть такие странности. Ваш набор тестов должен обеспечить вам уверенность в том, что система делает то, что вы ожидаете, на ожидаемых вами результатах. Если в некоторых случаях вы делаете странные вещи, вы заметите это достаточно быстро после того, как попробуете первый случай, и затем вы можете написать тест специально для этого случая (который обычно также охватывает целый класс подобных случаев).
источник
Возьмите крайние случаи плюс некоторый случайный ввод
Чтобы взять пример сортировки:
Если это работает быстро для них, вы можете быть совершенно уверены, что это будет работать для всех входных данных.
источник