2009-07-16 2 views
12

У меня есть веб-приложение PHP, построенное с использованием инфраструктуры CodeIgniter MVC. Я хочу протестировать различные классы контроллеров. Я использую Toast для модульного тестирования. У моих контроллеров нет состояния, все, что они обрабатывают, либо сохраняется в сеансе, либо передается на просмотр для отображения. Создание макетного объекта сеанса и проверка правильности его работы просто (просто создайте макет и добавьте его с помощью $ controller-> session = $ mock).Как проверить контроллеры с помощью CodeIgniter?

Что я не знаю, как работать с представлениями. В CodeIgniter, просмотры загружаются как:

$this->load->view($view_name, $vars, $return); 

Поскольку я не хочу, чтобы изменить код CI, я, хотя я мог бы создать макет загрузчик и заменить оригинал. И здесь проблема, я не могу найти способ получить новый класс из CI_Loader.

Если я не включать/библиотеки/файл Loader.php системы, класс CI_Loader не определен, и я не могу наследовать от него:

class Loader_mock extends CI_Loader 

Если я включить файл (используя require_once), Я получаю сообщение об ошибке:

Cannot redeclare class CI_Loader 

Похоже, что сам код CI не использует require_once по любой причине.

У кого-нибудь есть опыт работы с модульным тестированием приложений с поддержкой CodeIgniter?

Edit: Я попытался придать реальный объект загрузчика во время выполнения в макете класса, и переадресовать все вызовы и переменные с __call, __set, __get, __isset и __unset. Но, похоже, это не работает (я не получаю никаких ошибок, но просто нет вывода, т. Е. Пустой страницы из Toast). Вот код:

class Loader_mock 
{ 
    public $real_loader; 
    public $varijable = array(); 

    public function Loader_mock($real) 
    { 
     $this->real_loader = $real; 
    } 

    public function __call($name, $arguments) 
    { 
     return $this->real_loader->$name($arguments); 
    } 

    public function __set($name, $value) 
    { 
     return $this->real_loader->$name = $value; 
    } 

    public function __isset($name) 
    { 
     return isset($this->real_loader->$name); 
    } 

    public function __unset($name) 
    { 
     unset($this->loader->$name); 
    } 

    public function __get($name) 
    { 
     return $this->real_loader->$name; 
    } 

    public function view($view, $vars = array(), $return = FALSE) 
    { 
     $varijable = $vars; 
    } 
} 

ответ

4

В качестве альтернативы, вы можете сделать это:

$CI =& get_instance(); 
$CI = load_class('Loader'); 

class MockLoader extends CI_Loader 
{ 
    function __construct() 
    { 
     parent::__construct(); 
    } 
} 

Затем в контроллере сделать $ this-> нагрузки = новый MockLoader().

+0

Хорошая идея, спасибо. –

2

Мое настоящее решение - изменить код CodeIgniter, чтобы использовать require_once вместо require. Вот патч я собираюсь отправить CI разработчиков в случае кто-то должен делать то же самое, пока они не примут его:

diff --git a/system/codeigniter/Common.php b/system/codeigniter/Common.php 
--- a/system/codeigniter/Common.php 
+++ b/system/codeigniter/Common.php 
@@ -100,20 +100,20 @@ function &load_class($class, $instantiate = TRUE) 
     // folder we'll load the native class from the system/libraries folder. 
     if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT)) 
     { 
-    require(BASEPATH.'libraries/'.$class.EXT); 
-    require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT); 
+    require_once(BASEPATH.'libraries/'.$class.EXT); 
+    require_once(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT); 
       $is_subclass = TRUE; 
     } 
     else 
     { 
       if (file_exists(APPPATH.'libraries/'.$class.EXT)) 
       { 
-      require(APPPATH.'libraries/'.$class.EXT); 
+      require_once(APPPATH.'libraries/'.$class.EXT); 
         $is_subclass = FALSE; 
       } 
       else 
       { 
-      require(BASEPATH.'libraries/'.$class.EXT); 
+      require_once(BASEPATH.'libraries/'.$class.EXT); 
         $is_subclass = FALSE; 
       } 
     } 
1

Я не могу сильно помочь с тестированием, но я могу помочь вам продлить Библиотека CI.

Вы можете создать собственный MY_Loader класс внутри /application/libraries/MY_Loader.php.

<?php 
    class MY_Loader extends CI_Loader { 

    function view($view, $vars = array(), $return = FALSE) { 
     echo 'My custom code goes here'; 
    } 

    } 

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

Для получения дополнительной информации зарегистрируйте CI manual page for creating core system classes.

+3

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

1

Я впечатлен кодом, который вы пытаетесь использовать.

Итак, теперь мне интересно, как класс «Крюков» CodeIgniter может помочь вам в вашей проблеме?

http://codeigniter.com/user_guide/general/hooks.html

С наилучшими пожеланиями, Rein Гроот

+0

+1 для наконечника. Кажется, что это может быть выполнимо, но несколько неудобно. Я решил исправить CodeIgniter вместо этого - это простое изменение, и я надеюсь, что он однажды войдет в основной код CI (поскольку я не вижу причин, почему это не должно), поэтому мне не придется его исправлять, когда выпущены новые версии. –

0

Контроллер не должен содержать логику домена, поэтому модульные тесты здесь не имеют смысла.

Вместо этого я проверил бы контроллеры и представления с приемочными испытаниями.

+0

Ну ... пока в контроллерах нет логики домена, они * ответственны за управление вводом пользователя. И изменение состояния модельного слоя на основе этого ввода. Именно здесь можно было бы сосредоточиться на модульных тестах для контроллеров. –

+0

Я не думаю, что контроллер должен изменить состояние чего-либо в слое модели. Он должен просто получить запрос, а затем передать его на уровень модели (после обработки веб-безопасности и т. Д.). Если вы изменяете объекты или данные, я думаю, вам нужно будет протестировать. Но вы не должны этого делать в первую очередь. – Patrick

+1

Если вы передадите имя пользователя и пароль на уровень модели, вы измените его состояние. Или, если вы отправляете заголовок статьи для сохранения, вы также изменяете состояние слоя модели. И то, что вы называете «веб-безопасность», - это то, что должно выполняться на уровне модели (возможно, кроме предотвращения CSRF ... это может быть лучше сделано при инициализации экземпляра «Запрос»). Хотя, возможно, мы используем один и тот же термин для совершенно разных [концепций] (http://stackoverflow.com/a/5864000/727208). –

Смежные вопросы