Как писать тесты?
Ну тесты бываю разными: и зелеными и красными. ))
Все зависит от уровня абстракции, который хочется протестировать. Поэтому тесты и делятся на всяческие классификации. Например: юнит (модульные) тесты, интеграционные тесты и т.д. и т.п.
Я как ярый сторонник BDD использую два типа тестов feature (читай интеграционные тесты) и spec (читай юнит тесты)
Итак, features. Берем Behat и херачем кучу тестов по типу:
 Зашел на  главную,
 Потыкал чего-то в форме регистрации
 Зашел в профиль и увидел свое имя и фотку на странице
 Profit!
Вот в целом и получили: "Непосредственно регистрация с занесением в БД", только в этой ситуации нам абсолютно не важно что там в БД. Нам важно чтобы пользователь зарегистрировался и попал в закрытый раздел сайта.
Для таких тестов  хорошо иметь поддержку окружения (prodaction, development, test) в коде, чтобы например можно было капчу отключить или еще какую сложную лабуду не делать.
Если система замудрена до ужаса придется здесь для тестов все окружение поднимать. А лучще вообще отдать CI такое делать пусть друг трудится.
Плюс таких тестов например когда пишем сложный фронт - сложный бек  и еще более сложнейший бек-бек, то можно одним чохом протестировать работу всего сервиса.
Следующий уровень абстракции spec:
Если нам в интеграционных тестах было немного пофиг на БД. Она как бы пишет, но что так за структура абсолютно пофиг. То в случае со спекими нам ВАЩЕ ПОФИГ.
Мы берем наш класс (функцию) и проверяем что за результаты она отдает.  Вместо объектов, с которыми подопытный (объект тестируемого класса), даем ему резиновую бабу (моки и т.п), и смотрим на результаты нашего труда.
Главное при том и другом подходе нам без разницы, что у нас в кишках мы всегда тестируем публичные свойства системы. В первом случае это реакция на пользователя, во втором публичное API класса.
Вот как то так!
P.S  тесты надо бы писать до кода.
Ну вообще под тесты часто заводится отдельная бд, которая заполнена либо тестовыми данными, либо является клоном данных с рабочего сервера, либо заполняется данными перед выполнением теста и очищается после. 
Ну а с остальными случаями возможны разные варианты решения. Кого-то устроит просто true, которое вернули из PHPmailer::send(), но можно и получать по POP3 новые письма с ящика, на который отправляли, и ждать прихода нужного, с отвалом с непройденным тестом по таймауту. Зависит от вашего конкретного случая
Ну и разумеется у тестов проекта (не отдельной библиотеки, которая с данными не работает) будет сильная завязка на структуре бд, и при сильном изменении ее структуры/кода связанного с ней тесты упадут, и нужно будет их переписать, ровно как и связанный с ними код, для того они и нужны )