CodeIgniter4 测试入门

2020-08-17 17:42 更新

CodeIgniter的构建旨在使对框架和应用程序的测试尽可能简单。PHPUnit内置对它的支持,并且该框架提供了许多方便的辅助方法,以使您的应用程序的各个方面的测试尽可能轻松。

系统设置

安装phpUnit

CodeIgniter使用phpUnit作为所有测试的基础。有两种方法可以安装phpUnit以在系统中使用。

Composer

推荐的方法是使用Composer将其安装在项目中。尽管可以在全球范围内安装它,但我们不建议您这样做,因为随着时间的流逝,它可能会导致与系统上其他项目的兼容性问题。

确保系统上已安装Composer。在项目根目录(包含应用程序和系统目录的目录)中,从命令行键入以下内容:

> composer require --dev phpunit/phpunit

这将为您当前的PHP版本安装正确的版本。完成后,您可以通过键入以下内容运行该项目的所有测试:

> ./vendor/bin/phpunit

Phar

另一个选择是从phpUnit网站下载.phar文件。这是一个独立文件,应放置在项目根目录下。

测试您的应用程序

PHPUnit配置

框架phpunit.xml.dist在项目根目录中有一个文件。这控制了框架本身的单元测试。如果您提供自己的phpunit.xml,它将超越此。

phpunit.xml应该排除的system文件夹,以及任何vendorThirdParty文件夹,如果你是单元测试您的应用程序。

测试类

为了利用所提供的其他工具,您的测试必须扩展CIUnitTestCase。默认情况下,所有测试均应位于tests / app目录中。

要测试新库Foo,您可以在tests / app / Libraries / FooTest.php中创建一个新文件:

<?php namespace App\Libraries;


use CodeIgniter\Test\CIUnitTestCase;


class FooTest extends CIUnitTestCase
{
    public function testFooNotBar()
    {
        . . .
    }
}

要测试您的模型之一,您可能会在以下代码中得到以下结果tests/app/Models/OneOfMyModelsTest.php

<?php namespace App\Models;


use CodeIgniter\Test\CIUnitTestCase;


class OneOfMyModelsTest extends CIUnitTestCase
{
    public function testFooNotBar()
    {
        . . .
    }
}

您可以创建适合您的测试样式/需求的任何目录结构。为测试类命名时,请记住app目录是App名称空间的根,因此您使用的任何类都必须具有相对于的正确名称空间App

注解

测试类并非严格要求使用名称空间,但是它们有助于确保没有类名冲突。

测试数据库结果时,必须使用CIDatabaseTestClass类。

附加断言

CIUnitTestCase 提供了其他可能有用的单元测试断言。

assertLogged($ level,$ expectedMessage)

确保您希望实际记录的内容是:

$config = new LoggerConfig();
$logger = new Logger($config);


... do something that you expect a log entry from
$logger->log('error', "That's no moon");


$this->assertLogged('error', "That's no moon");

assertEventTriggered($ eventName)

确保您希望触发的事件实际上是:

Events::on('foo', function($arg) use(&$result) {
    $result = $arg;
});


Events::trigger('foo', 'bar');


$this->assertEventTriggered('foo');

assertHeaderEmitted($ header,$ ignoreCase = false)

确保实际上发出了标头或cookie:

$response->setCookie('foo', 'bar');


ob_start();
$this->response->send();
$output = ob_get_clean(); // in case you want to check the actual body


$this->assertHeaderEmitted("Set-Cookie: foo=bar");

注意:与此相关的测试案例应在PHPunit中作为单独的进程运行

assertHeaderNotEmitted($ header,$ ignoreCase = false)

确保没有发出标题或cookie:

$response->setCookie('foo', 'bar');


ob_start();
$this->response->send();
$output = ob_get_clean(); // in case you want to check the actual body


$this->assertHeaderNotEmitted("Set-Cookie: banana");

注意:与此相关的测试案例应在PHPunit中作为单独的进程运行

assertCloseEnough($ expected,$ actual,$ message ='',$ tolerance = 1)

对于延长的执行时间测试,请测试预期时间与实际时间之间的绝对差是否在规定的公差内:

$timer = new Timer();
$timer->start('longjohn', strtotime('-11 minutes'));
$this->assertCloseEnough(11 * 60, $timer->getElapsedTime('longjohn'));

上述测试将允许实际时间为660或661秒。

assertCloseEnoughString($ expected,$ actual,$ message ='',$ tolerance = 1)

对于延长的执行时间测试,测试预期时间与实际时间之间的绝对差(格式为字符串)是否在规定的公差范围内:

$timer = new Timer();
$timer->start('longjohn', strtotime('-11 minutes'));
$this->assertCloseEnoughString(11 * 60, $timer->getElapsedTime('longjohn'));

上述测试将允许实际时间为660或661秒。

访问受保护的/私有的属性

测试时,可以使用以下setter和getter方法访问要测试的类中的受保护的方法和私有方法以及属性。

getPrivateMethodInvoker($ instance,$ method)

使您可以从类外部调用私有方法。这将返回一个可以调用的函数。第一个参数是要测试的类的实例。第二个参数是您要调用的方法的名称。

// Create an instance of the class to test
$obj = new Foo();


// Get the invoker for the 'privateMethod' method.
    $method = $this->getPrivateMethodInvoker($obj, 'privateMethod');


// Test the results
    $this->assertEquals('bar', $method('param1', 'param2'));

getPrivateProperty($ instance,$ property)

从类的实例中检索私有/受保护的类属性的值。第一个参数是要测试的类的实例。第二个参数是属性的名称。

// Create an instance of the class to test
$obj = new Foo();


// Test the value
$this->assertEquals('bar', $this->getPrivateProperty($obj, 'baz'));

setPrivateProperty($ instance,$ property,$ value)

在类实例中设置一个受保护的值。第一个参数是要测试的类的实例。第二个参数是要为其设置值的属性的名称。第三个参数是将其设置为的值:

// Create an instance of the class to test
$obj = new Foo();


// Set the value
$this->setPrivateProperty($obj, 'baz', 'oops!');


// Do normal testing...

模拟服务

您通常会发现您需要模拟app / Config / Services.php中定义的服务之一,以将测试限制为仅涉及代码,同时模拟服务的各种响应。在测试控制器和其他集成测试时尤其如此。该服务类提供了两个方法,使这个简单的:injectMock()reset()

injectMock()

此方法允许您定义Services类将返回的确切实例。您可以使用它来设置服务的属性,使其以某种方式运行,或者将服务替换为模拟类。

public function testSomething()
{
    $curlrequest = $this->getMockBuilder('CodeIgniter\HTTP\CURLRequest')
                        ->setMethods(['request'])
                        ->getMock();
    Services::injectMock('curlrequest', $curlrequest);


    // Do normal testing here....
}

第一个参数是您要替换的服务。该名称必须与Services类中的函数名称完全匹配。第二个参数是要替换为的实例。

reset()

从Services类中删除所有模拟的类,使其恢复到原始状态。

流过滤器

CITestStreamFilter提供了这些辅助方法的替代方法。

您可能需要测试难以测试的事物。有时,捕获流(例如PHP自己的STDOUT或STDERR)可能会有所帮助。将CITestStreamFilter帮助您从您所选择的流捕获输出。

在您的一个测试用例中演示此示例:

public function setUp()
{
    CITestStreamFilter::$buffer = '';
    $this->stream_filter = stream_filter_append(STDOUT, 'CITestStreamFilter');
}


public function tearDown()
{
    stream_filter_remove($this->stream_filter);
}


public function testSomeOutput()
{
    CLI::write('first.');
    $expected = "first.\n";
    $this->assertEquals($expected, CITestStreamFilter::$buffer);
}
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号