Кажется, вы понимаете базовую структуру / синтаксис написания тестов и модульного тестирования. Код CodeIgniter не должен ничем не отличаться от тестирования кода без CI, поэтому я хочу сосредоточиться на ваших основных проблемах / проблемах ...
У меня были подобные вопросы не так давно с PHPUnit. Как человек без формального обучения, я обнаружил, что мышление в модульном тестировании поначалу казалось абстрактным и неестественным. Я думаю, что основная причина этого - в моем случае, и, вероятно, ваша тоже из вопроса) - то, что вы до сих пор не сосредоточились на ДЕЙСТВИТЕЛЬНО работе над разделением проблем в вашем коде.
Утверждения тестирования кажутся абстрактными, потому что большинство ваших методов / функций, вероятно, выполняют несколько различных дискретных задач. Успешный менталитет тестирования требует изменения в том, как вы думаете о своем коде. Вы должны перестать определять успех с точки зрения "это работает?" Вместо этого вы должны спросить: «Работает ли он, будет ли он хорошо играть с другим кодом, разработан ли он таким образом, чтобы сделать его полезным для других приложений, и могу ли я проверить, работает ли он?»
Например, ниже приведен упрощенный пример того, как вы, вероятно, написали код до этого момента:
function parse_remote_page_txt($type = 'index')
{
$remote_file = ConfigSingleton::$config_remote_site . "$type.php";
$local_file = ConfigSingleton::$config_save_path;
if ($txt = file_get_contents($remote_file)) {
if ($values_i_want_to_save = preg_match('//', $text)) {
if (file_exists($local_file)) {
$fh = fopen($local_file, 'w+');
fwrite($fh, $values_i_want_to_save);
fclose($fh);
return TRUE;
} else {
return FALSE;
}
} else {
return FALSE;
}
}
Точно, что здесь происходит, не важно. Я пытаюсь проиллюстрировать, почему этот код сложно протестировать:
Он использует класс конфигурации singleton для генерации значений. Успех вашей функции зависит от значений из синглтона, и как вы можете проверить, что эта функция работает правильно в полной изоляции, когда вы не можете создать новые объекты конфигурации с другими значениями? Лучшим вариантом может быть передача вашей функции аргумента $config
, который состоит из объекта конфигурации или массива, значениями которого вы можете управлять. Это широко называется « Внедрение зависимостей », и обсуждаются эти методы во всех сетях.
Обратите внимание на вложенные IF
операторы. Тестирование означает, что вы закрываете каждую исполняемую строку каким-то тестом. Когда вы вкладываете операторы IF, вы создаете новые ветви кода, которые требуют нового пути тестирования.
Наконец, вы видите, как эта функция, хотя она, кажется, выполняет одну вещь (анализирует содержимое удаленного файла), фактически выполняет несколько задач? Если вы усердно разделяете свои проблемы, ваш код становится бесконечно более тестируемым. Гораздо более тестируемый способ сделать то же самое будет ...
class RemoteParser() {
protected $local_path;
protected $remote_path;
protected $config;
/**
* Class constructor -- forces injection of $config object
* @param ConfigObj $config
*/
public function __construct(ConfigObj $config) {
$this->config = $config;
}
/**
* Setter for local_path property
* @param string $filename
*/
public function set_local_path($filename) {
$file = filter_var($filename);
$this->local_path = $this->config->local_path . "/$file.html";
}
/**
* Setter for remote_path property
* @param string $filename
*/
public function set_remote_path($filename) {
$file = filter_var($filename);
$this->remote_path = $this->config->remote_site . "/$file.html";
}
/**
* Retrieve the remote source
* @return string Remote source text
*/
public function get_remote_path_src() {
if ( ! $this->remote_path) {
throw new Exception("you didn't set the remote file yet!");
}
if ( ! $this->local_path) {
throw new Exception("you didn't set the local file yet!");
}
if ( ! $remote_src = file_get_contents($this->remote_path)) {
throw new Exception("we had a problem getting the remote file!");
}
return $remote_src;
}
/**
* Parse a source string for the values we want
* @param string $src
* @return mixed Values array on success or bool(FALSE) on failure
*/
public function parse_remote_src($src='') {
$src = filter_validate($src);
if (stristr($src, 'value_we_want_to_find')) {
return array('val1', 'val2');
} else {
return FALSE;
}
}
/**
* Getter for remote file path property
* @return string Remote path
*/
public function get_remote_path() {
return $this->remote_path;
}
/**
* Getter for local file path property
* @return string Local path
*/
public function get_local_path() {
return $this->local_path;
}
}
Как видите, каждый из этих методов класса обрабатывает определенную функцию класса, которая легко тестируется. Работал ли удаленный поиск файлов? Нашли ли мы значения, которые пытались проанализировать? И т.д. Внезапно эти абстрактные утверждения кажутся гораздо более полезными.
ИМХО, чем больше вы погружаетесь в тестирование, тем больше понимаете, что это больше о хорошем дизайне кода и разумной архитектуре, чем просто о том, что все работает так, как ожидалось. И вот здесь преимущества ООП действительно начинают сиять. Вы можете просто протестировать процедурный код, но для большого проекта с взаимозависимым тестированием частей есть способ обеспечить хороший дизайн. Я знаю, что это может быть приманкой для некоторых процедурных людей, ну да ладно.
Чем больше вы будете тестировать, тем больше вы будете писать код и спрашивать себя: «Смогу ли я это проверить?» А если нет, то вы, вероятно, измените структуру тогда и там.
Однако код не обязательно должен быть элементарным, чтобы его можно было тестировать. Заглушка и насмешка позволяет вам проверять внешние операции, успех или неудача которых совершенно не контролируются. Вы можете создать fixtures для тестирования операций с базой данных и всего остального.
Чем больше я тестирую, тем больше понимаю, что, если мне тяжело тестировать что-то, это, скорее всего, потому, что у меня есть основная проблема проектирования. Если я это исправлю, это обычно приводит ко всем зеленым столбцам в моих результатах теста.
Наконец, вот пара ссылок, которые действительно помогли мне начать думать в удобной для тестирования форме. Первый - это ненормативный список того, что НЕ нужно делать, если вы хотите написать тестируемый код . На самом деле, если вы просматриваете весь этот сайт, вы найдете много полезных вещей, которые помогут вам перейти на 100% покрытие кода. Другая полезная статья - это обсуждение внедрения зависимостей .
Удачи!