Laravel之PHPUnit

laravel vs phpunit

今天主要讨论的是Laravel中如何编写单元测试的问题,也由于Laravel内置的测试框架就是采用的PHPUnit,所以今天的主角也就是PHPUnit。

程序员为什么要测试?

首先测试是一个比较大的概念,今天我们只讨论单元测试。

作为开发保证自己编写代码的正确性是最基本的,当我们向QA提测前如果做好自测是最基本的,而如何漂亮的做好自测?如何快速的检查某个函数边界条件的正确性?单元测试可以帮我们做到,而做到的还不止一点点,。试想一下,如果经历无Bug上线这样美好的事儿,那于我们来讲是多么美好呢!总之,去编写单元测试吧,让我们更加自信的去提测,一切都会变得美好!

而美好的愿望总是会遇到一些阻碍,比如在国内很少有编写单元测试这样的氛围,而也很少存在那些主动的开发人员将单元测试普及开来,导致这样美好的愿望阻碍重重。

如果你们的项目有用到Laravel这样如此Graceful的框架,那么编写单元测试将会非常容易,而这也是今天讨论的重点。

更多关于PHP测试相关的内容,可以看我之前的这篇文章PHP与测试

Laravel的PHPUnit
我们编写的所有测试用例将会存放在Laravel框架下的tests目录下。默认情况下,Laravel为我们准备了两个文件TestCase.php和ExampleTest.php,TestCase.php将作为我们编写的测试用例类的基类,继承了TestCase.php后,我们将可以很方便的使用Laravel以PHPUnit为基础为我们封装的各种方法,使得我们的测试方便至极!真的是方便至极!
比如ExampleTest.php : 就是用来测试那个非常经典的Laravel默认页

<?php
    //...省略
    public function testBasicExample()
    {
        $this->visit('/')
             ->see('Laravel 5');
    }
    //...省略
  • $this->visit(‘/’); : 模拟一个http请求,访问路由/
  • $this->see(‘Laravel 5’); : 检查返回的html源代码中是否存在Laravel 5这个字符串

除了tests目录下的TestCase.php和ExampleTest.php之外还有一个很重要的PHPUnit配置文件,即phpunit.xml,既然是配置文件,则在其中定义了一些这样的事儿,比如以Test.php为结尾的文件将会被作为测试用例执行,设定Laravel的ENV为testing等等,基本上默认的phpunit.xml是不需要改动的,可以直接拿来用。

好,上面简单的做一下介绍,下面让我们正式讨论如何在Laravel中编写测试用例。

Get Start

安装phpunit命令(可选)
我们会需要phpunit的全局可执行命令,作为我们测试的命令。不过这个步骤是可选的,原因在于你会发现vendor/bin目录下存在这个命令,所以如果不全局安装是可以直接使用这个命令的。

具体的安装步骤:

//使用composer全局安装phpunit,后面的版本号参考composer.json中的phpunit版本,比如我的是4.8.27
composer global require phpunit/phpunit 4.8.27
//打开vim ~/.profile,追加如下环境变量
export PATH=~/.composer/vendor/bin:$PATH
source ~/.profile
//测试是否安装成功
phpunit --version

修改TestCase.php
修改你的$baseUrl为你的可执行的链接,一定要保证你的机器是可以访问到此链接的哈,不确定的话,可以ping一下。

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class TestCase extends Illuminate\Foundation\Testing\TestCase
{
    /**
     * The base URL to use while testing the application.
     * @var string
     */
    protected $baseUrl = 'http://yourhost.com';

    public function setUp()
    {
        parent::setUp();
    }

    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';
        $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
        return $app;
    }
}

执行测试用例
既然已经准备OK了,那么就可以在项目根目录下执行phpunit命令了,如果能看到下面的一幕则我们已经完成了测试的第一步!恭喜!

➜ laravel_project git:(master) phpunit
PHPUnit 4.8.27 by Sebastian Bergmann and contributors.
..
Time: 361 ms, Memory: 13.75MB
OK (2 tests, 2 assertions)

编写我们的第一个测试
假如我的项目中存在一个API接口,访问路由为/api/username,返回的数据结构为:

{
    code: 0,
    message: "操作成功。",
    data: {
        "name" : "xuwenzhi"
    }
}

此时我可以通过命令来创建一个测试用例(测试用例的名称一定要Test.php结尾,这个规则是在phpunit.xml中定义的)

php artisan make:test UserNameTest

此时,tests目录下会出现我们新建的测试用例UserNameTest.php,打开它,编辑如下

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserNameTest extends TestCase
{
    protected $route = '/api/username';
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->json('GET', $this->route);
        $this->assertResponseOk();
        $this->assertResponseStatus(200);
        $this->seeJson([
            'code' => 0,
            'message' => '操作成功',
            'data' => [
                'name' => 'xuwenzhi'
            ],
        ])->seeJsonStructure([
            'code',
            'data' => [
                'name',
            ],
            'message',
        ]);
        $content = $this->decodeResponseJson();//将返回结果做json_decode()
        print_r($content);
    }
}

执行如下命令,即可单独测试该用例

phpunit tests/UserNameTest.php

其他补充

Laravel提供的API
上面我们写了一个简单的测试用例,其中我们注意到$this->json($method, $uri, array $data = [], array $headers = [])方法,调用此方法Laravel将会自动为我们发起一个HTTP请求,对于请求方式GET/POST/其他则通过第一个参数指定,第二个参数为路由,第三个参数为请求携带的参数,第四个参数为headers相关。

对于这样的函数还有很多,选择性也很多,比如:

//发起任意类型的请求
$this->call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null);

//发起GET请求
$this->get($uri, array $headers = []);

//发起POST请求
$this->post($uri, array $data = [], array $headers = []);

//同样的,任何一个HTTP METHOD请求均可以这样的方式发起。

//发起针对json API的请求(即返回数据为json类型)
$this->json('GET', '/api/agent/config?city_id=110000');

//打印接口返回的数据
$this->dump();

//获取接口响应内容
$response = $this->call('GET', $this->route);
$content = $response->getContent()//$content即为接口返回数据

Laravel提供的Asserting(断言)
什么叫断言?也就是我们对请求结果的验证结果,也就是一个true or false的过程,上面我们在编写第一个测试用例中的seeJson()和seeJsonStructure()就是Laravel为我们提供的断言函数,用于测试API返回的json是否是预期的结果。

//响应是否成功
$this->assertResponseOk();
//状态码是否是预期的
$this->assertReponseStatus($code)

//判断值是否相等
$this->assertEquals(true);

//...等等等

除此以外,PHPUnti Framework的所有断言均可使用,具体可以查看PHPUnit官方手册Appendix A. Assertions

综上
Laravel phpunit基本的内容也就是这些,当然所有的东西并不会风平浪静的,总会有一些坑,预知坑如何填,点Laravel之PHPUnit问题篇

开展团队内部测试相关的活动还是很有意义的,比如TDD。我们团队也正在朝着这个方向发展,所以去编写测试吧!

一不小心写了这么久,这么多字,该睡了。

Laravel之PHPUnit
Tags:         

发表评论

电子邮件地址不会被公开。 必填项已用*标注