Laravel+phpunit,实践测试驱动开发

TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。

PHPUnit是一个面向PHP程序员的测试框架,这是一个xUnit的体系结构的单元测试框架,配合Laravel,写单元测试和功能测试非常方便。

1、安装phpunit

wget http://phar.phpunit.cn/phpunit.phar

chmod +x phpunit.phar

sudo mv phpunit.phar /usr/local/bin/phpunit

phpunit -version

出现以下版本信息安装成功 PHPUnit 6.5.3 by Sebastian Bergmann and contributors.`

2、Laravel集成测试和执行测试

Laravel使用PHPUnit对测试提供支持是开箱即用的,这点比我以前接触过的Yii要强大,所以说技术选型中生态很重要。测试配置文件phpunit.xml(项目根目录)已经为应用设置好了。框架还提供了很多辅助函数从而允许你对应用进行更加富有表现力的测试。

phpunit //执行所有 test 方法

phpunit tests/Unit/ //执行tests/Unit/下所有单元方法

phpunit tests/Unit/ExampleTest.php //执行单个文件单元测试

phpunit tests/Feature/ExampleTest.php//执行单个文件功能测试

phpunit --filter "testExample" tests/Unit/HelloTest //执行单个文件中方法测试

3、编写单元测试和功能测试例子,编写文件以Test.php结尾,

单元测试是用来测试项目中的Model、Repository、Service等业务逻辑代码,而功能测试是看测试代码是否能够正确访问到Controller并断言Controller的行为:是否redirect 到了目标URL、返回了指定的视图或者是跳转并携带着造成跳转的错误信息。

单元测试:

php artisan make:test HelloTest --unit

功能测试,不带参数–unit,默认是功能测试

php artisan make:test HelloTest

编写类中的方法,以test开头,function testXXX()

4、环境phpunit.xml

运行测试的时候,Laravel 会自动设置环境为 testing,这是因为 phpunit.xml 中定义了环境变量。在测试时还会自动配置 Session 和缓存驱动为 array,这意味着测试时不会持久化存储会话和缓存。
在项目根目录下创建一个 .env.testing 文件,该文件会在运行 PHPUnit 测试或执行带 –env=testing 开关的 Artisan 命令时覆盖 .env 文件中的环境变量。确保在运行命令之前使用 Artisan 命令 config:clear 清除配置缓存。

5、禁用中间件

当测试应用时,在测试中禁中间件。可以隔离的测试路由和控制器而免除中间件的顾虑。引入 WithoutMiddleware trait 来在测试类中禁用所有的中间件:

use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
    use WithoutMiddleware; //全局禁用中间件

    public function testBasicTest()
    {
        $this->withoutMiddleware(); //方法中禁用中间件
        $this->assertTrue(true);
    }

}

RefreshDatabase 是重置清空数据库,建议phpunit连接的数据库不要和开发和测试共用,防止误删除表数据。

6、Api测试

Laravel 为你的 PHPUnit 测试提供了各种各样的自定义断言方法。json,get,post,put 和 delete 这些测试方法返回的响应都可以使用这些断言方法:

class ExampleTest extends TestCase
{

    public function testBasicTest()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}
//$response响应断言
$response->assertCount($num, $data); //断言data的数量为num
$response->assertTrue($condition); //断言运行结果是否为true
$response->assertSee($value);  //断言给定的字符串包含在响应中
$response->assertDoneSee($value); //断言给定的字符串包不含在响应中
$response->assertEquals($expected, $actual); //断言两个对象是否相等
$response->assertSame($expecter, $actual); //断言两个对象的类型和值是否相等
$response->assertSuccessful();   // 断言该响应具有成功的状态码。
$response->assertStatus($code);  // 断言该响应具有指定的状态码。
$response->assertRedirect($uri);     // 断言该响应被重定向至指定的 URI。
$response->assertHeader($headerName, $value = null);     // 断言该响应存在指定的标头。
$response->assertCookie($cookieName, $value = null);     // 断言该响应包含了指定的 Cookie。
$response->assertPlainCookie($cookieName, $value = null);    // 断言该响应包含了指定的 Cookie(未加密)。
$response->assertSessionHas($key, $value = null);    // 断言该 Session 包含指定的数据。
$response->assertSessionHasErrors(array $keys, $errorBag = 'default');   // 断言该 Session 包含指定的字段的错误信息。
$response->assertSessionMissing($key);   // 断言该 Session 不包含指定的键。
$response->assertJson(array $data);  // 断言该响应包含指定的 JSON 数据。
$response->assertJsonFragment(array $data);  // 断言该响应包含指定的 JSON 片段。
$response->assertJsonMissing(array $data);   // 断言该响应不包含指定的 JSON 片段。
$response->assertExactJson(array $data);     // 断言该响应包含完全匹配指定的 JSON 数据。
$response->assertJsonStructure(array $structure);    // 断言该响应存在指定的 JSON 结构。
$response->assertViewIs($value);     // 断言该视图响应的视图名称为指定的值。
$response->assertViewHas($key, $value = null);   // 断言该视图响应存在指定的数据。

养成单元测试和功能测试的习惯,是保证代码质量的重要手段,实际在写代码中,把通过手动访问页面低效测试,改为单测进行一段时间,会发现会爱上单元测试和功能测试。

参考文档: https://laravel-china.org/docs/laravel/5.5/testing/1338