模拟 HTTP 请求进行单元测试

Mat*_*att 3 php phpunit unit-testing httprequest

我正在编写一个 MVC 框架,它利用一个库来帮助管理输入变量(例如 get、post 等)。

现在,我正在通过分别将 POST 和 GET 变量的值强制写入 $_POST 和 $_GET 超全局变量来测试 POST 和 GET 变量,但在测试 PUT 和 DELETE 请求变量时遇到了障碍。

例子:

public function testGET()
{
    $_GET['test'] = 'test value';

    $get_value = Input::get('test', '[default value]');

    assertEquals('test value', $get_value);
}
Run Code Online (Sandbox Code Playgroud)

是否有一个好方法来模拟/模拟这些变量或使用各种可用的方法(特别是 GET、POST、PUT、DELETE)来模拟 HTTP 请求?

Dar*_*ook 5

我有时觉得人们过度使用可测试性设计,最终得到的代码中 80% 的复杂性似乎只是为了更容易测试。我觉得对于单元测试变得混乱的东西,切换到功能测试通常会更好。

但对于 PHP 超级全局变量,我放弃了。所以我尽量避免在任何框架代码中使用 $_POST 或 $_GET,而是使用参数,或者,如果这使代码太难看,则使用类成员或静态成员。

因此,您的 get() 函数可以重写如下:

public static function get($name,$default,$GET=null)
{
  if($GET===null)$GET=$_GET;  //For non-test code
  if(!array_key_exists($name,$GET))return $default;
  return $GET[$name];
}
Run Code Online (Sandbox Code Playgroud)

那么你的测试就变成:

public function testGET()
{
    $GET=array('test' => 'test value');
    $get_value = Input::get('test', '[default value]', $GET);
    $this->assertEquals('test value', $get_value);
}
Run Code Online (Sandbox Code Playgroud)

当对 Input::get() 的调用深埋在调用层次结构中时,这很快就会变得尴尬;那么静态类成员更好。要使用类成员,可以在函数中传递 $GET init()。但如果有一个init()函数不合适,可以这样做:

public static $GET=null;

public static function get($name,$default)
{
  if(self::$GET===null)self::$GET=$_GET;  //Init on first call
  if(!array_key_exists($name,self::$GET))return $default;
  return $GET[$name];
}
Run Code Online (Sandbox Code Playgroud)

然后测试就变成:

public function testGET()
{
    Input::$GET=array('test' => 'test value');
    $get_value = Input::get('test', '[default value]');
    $this->assertEquals('test value', $get_value);
}
Run Code Online (Sandbox Code Playgroud)

旁白:我喜欢这种方法的另一个(非测试)原因是它允许客户端代码选择使用$_GET$_POST$_REQUEST等。所以我将命名静态变量self::$INPUT,并有init()一个用户需要传入的函数$_POST或者$_GET或者他们想要的任何东西。

您问题的后半部分询问有关测试 PUT 和 DELETE 数据的问题。但是您没有说明实际代码中如何处理它。您是否有一些类似于此答案中显示的代码:/sf/answers/376241701/

如果是这样,一种方法是模拟该getContent()函数(如此处所示)以返回硬编码文本,而不是从php://input. 另一种方法是使用另一个函数(在您的实际类中),主要用于测试,该函数$this->content在将使用该变量的第一个调用之前进入并设置。($this->content这个答案基本上就像self::$INPUT我之前提到的变量一样。)