Про тестирование программы во время написания

25.12.2017

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

(Я про это уже писал в разных местах, в том числе (в сокращенном виде) — в моем тексте про тестирование, но, думаю, это заслуживает отдельного поста.)

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

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

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

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

Когда вы рассматриваете конкретную строчку программы, вы можете понять, что она работает неверно при определенных значениях тех переменных, которые участвуют в конкретном месте программы. Если вы это поняли, строго говоря это еще не обозначает, что у вас баг в программе — возможно, что эти переменные в принципе не могут принимать таких значений при допустимых входных данных. Тогда, с одной стороны, полезно все-таки доосозновать это, понять, какие входные данные на самом деле приводят к ошибке в этой строке, и есть ли такие входные данные вообще (а если их нет — то это в общем-то и не баг); но с другой стороны, даже если таких входных данных нет, все равно может иметь смысл обнаруженную проблему исправить — вдруг вы на самом деле неправильно раскрутили ситуацию и есть какой-нибудь особый вариант входных данных, который в итоге приводит к этой ошибке; или вдруг такой вариант появится позже, когда вы чуть-чуть исправите свою программу (например, если позже вы обнаружите другой баг и будете его исправлять).

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

Почему я пишу, что это надо делать параллельно с написание кода? Это можно делать и потом, но потом уже намного сложнее сосредоточиться и пройти по всему коду, тщательно продумывая особенности всех строк. А во время написания кода вы и так и так проходитесь по всему коду, поэтому самое время и подумать, как его можно взломать. На самом деле никто вам не мешает после написания программы повторно просмотреть код и тщательно продумать все это еще раз (и это безусловно полезно в сложных задачах, а также если у вас что-то не работает), но по моему опыту при таком повторном проходе у вас уже падает внимательность, поэтому проверка при написании кода еще более важна.

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

…Десять лет назад мы готовили IV Нижегородскую городскую олимпиаду школьников по информатике. Я писал решение задачи 4 «Мертвый код» (условия задая по ссылке выше в архиве rar, но они сейчас не особо важны). Пока я писал решение, я осознал, что в задаче есть некоторая подстава. Я аккуратно эту подставу учел и запомнил. Продолжив писать решение, я осознал, что есть и еще одна подстава. Ее я тоже учел, запомнил, и, дописав решение, написал письмо Роме Тимушеву, который был «ответственным» за эту задачу, в том числе готовил тесты к ней. В письме я упомянул эти две подставы и написал, что надо бы добавить тестов на них. (Рома скорее всего и так их добавил бы, но я решил подстраховаться.) В итоге эту задачу полностью не решил никто (ну она и задумывалась как довольно сложная), причем победитель олимпиады не учел одну подставу, а участник, занявший второе место, не учел (не учла) вторую из этих подстав.

…Вообще, тут просится еще длинный текст насчет того, как исправлять найденные проблемы в коде — что далеко не всегда (или даже почти никогда) явный if не является правильным решением — но текст тут уже и так длинный, а про это я уже фрагментарно писал и в тексте про тестирование, и в других местах и, может быть, еще и напишу в этом блоге.


Мой курс по алгоритмическому программированию (и подготовке к олимпиадам) для школьников, студентов и всех желающих — algoprog.ru