如何使用CodeIgniter测试控制器?

时间:2009-07-16 18:23:32

标签: php codeigniter

我有一个使用CodeIgniter MVC框架构建的PHP Web应用程序。我想测试各种控制器类。我正在使用Toast进行单元测试。我的控制器没有状态,它们处理的所有内容都保存到会话中或传递给视图显示。创建一个模拟会话对象并测试它是否正常工作很简单(只需创建一个模拟对象并使用$ controller-> session = $ mock注入它)。

我不知道的是,如何使用视图。在CodeIgniter中,视图加载为:

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

由于我不想改变CI代码,我可以创建一个模拟Loader并替换原始代码。这就是问题所在,我找不到从CI_Loader派生新类的方法。

如果我不包含system / libraries / Loader.php文件,那么CI_Loader类是未定义的,我不能继承它:

class Loader_mock extends CI_Loader

如果我确实包含该文件(使用require_once),我会收到错误:

Cannot redeclare class CI_Loader

看起来CI代码本身不会出于任何原因使用require_once。

有没有人有过使用CodeIgniter驱动的应用程序进行单元测试的经验?

编辑:我试图在运行时将一个真正的加载器对象注入一个模拟类,并使用__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;
    }
}

5 个答案:

答案 0 :(得分:4)

或者,你可以这样做:

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

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

然后在您的控制器中执行$ this-> load = new MockLoader()。

答案 1 :(得分: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;
                }
        }

答案 2 :(得分: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)

我对您尝试使用的代码印象深刻。

所以现在我想知道CodeIgniter的'Hooks'类对你的问题有什么帮助?

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

亲切的问候, Rein Groot

答案 4 :(得分:0)

控制器不应包含域逻辑,因此单元测试没有任何意义。

相反,我会通过验收测试来测试控制器和视图。