1123

1123

1

Не выполняйте никакую сложную работу в конструкторе, также не обращайтесь в конструкторе к каким-либо внешним для программы объектам, таким как файловая система, стандартные потоки ввода и вывода, базы данных и т.д. В конструкторе должна быть только простейшая инициализация полей класса, автономная или в зависимости от параметров конструктора. Это связано с тестированием класса и гибкостью дизайна.

На самом деле, почти всегда класс, который легко тестировать, имеет гибкий дизайн и его удобно использовать, и наоборот. Если у класса в конструкторе

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

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

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

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

На самом деле, нашему алгоритмическому классу нужны от класса, работающего с базой данных, лишь конкретные данные, которые тот берет из базы данных,

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

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

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

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

различных таблиц.

Report Page