PHPUnit实践三(构建模块化的测试单元)

33次阅读

共计 6441 个字符,预计需要花费 17 分钟才能阅读完成。

本系列教程所有的 PHPUnit 测试基于 PHPUnit6.5.9 版本,Lumen 5.5 框架
目录结构
模块下的目录是符合 Lumen 的模块结构的如:Controllers、Models、Logics 等是 Lumen 模块目录下的结构目录如果有自己的目录同级分配即可,如我这里的 Requests
整体结构
├── BaseCase.php 重写过 Lumen 基类的测试基类,用于我们用这个基类做测试基类,后续会说明
├── bootstrap.php tests 自动加载文件
├── Cases 测试用例目录
│ └── Headline 某测试模块
│ ├── logs 日志输出目录
│ ├── PipeTest.php PHPUnit 流程测试用例
│ ├── phpunit.xml phpunit 配置文件 xml
│ └── README.md 本模块测试用例说明
├── ExampleTest.php 最原始测试 demo
└── TestCase.php Lumen 自带的测试基类
某模块的目录结构
Headline // 某测试模块测试用例目录
├── Cache
├── Controllers
│   ├── ArticleTest.php
│   ├── …
├── Listeners
│   └── MyListener.php
├── Logics
├── Models
│   ├── ArticleTest.php
│   ├── …
├── README.md
├── Requests
│   ├── ArticleTest.php
│   ├── …
├── logs // 日志和覆盖率目录
│   ├── html
│   │   ├── …
│   │   └── index.html
│   ├── logfile.xml
│   ├── testdox.html
│   └── testdox.txt
├── phpunit-debug-demo.xml //phpunit.xml 案例
├── phpunit-debug.xml // 改名后测试用的
└── phpunit.xml // 正式用的 xml 配置
BaseCase.php
<?php
namespace Test;

use Illuminate\Database\Eloquent\Factory;

class BaseCase extends TestCase
{
protected $seeder = false;

const DOMAIN = “http://xxx.com”;
const API_URI = [];
const TOKEN = [
‘local’ => ‘token*’,
‘dev’ => ‘token*’,
‘prod’ => ” // 如果测试真实请填写授权 token
];

/**
* 重写 setUp
*/
public function setUp()
{
parent::setUp();

$this->seeder = false;
if (method_exists($this, ‘factory’)) {
$this->app->make(‘db’);
$this->factory($this->app->make(Factory::class));

if (method_exists($this, ‘seeder’)) {
if (!method_exists($this, ‘seederRollback’)) {
dd(“ 请先创建 seederRollback 回滚方法 ”);
}
$this->seeder = true;
$this->seeder();
}
}
}

/**
* 重写 tearDown
*/
public function tearDown()
{
if ($this->seeder && method_exists($this, ‘seederRollback’)) {
$this->seederRollback();
}

parent::tearDown();
}

/**
* 获取地址
* @param string $apiKey
* @param string $token
* @return string
*/
protected function getRequestUri($apiKey = ‘list’, $token = ‘dev’, $ddinfoQuery = true)
{
$query = “?token=” . static::TOKEN[strtolower($token)];
if ($ddinfoQuery) {
$query = $query . “&” . http_build_query(static::DDINFO);
}

return $apiUri = static::DOMAIN . static::API_URI[$apiKey] . $query;
}
}
phpunit-debug-demo.xml
本文件是我们单独为某些正在测试的测试用例,直接编写的 xml,可以不用来回测试,已经测试成功的测试用例了,最后全部编写完测试用例,再用正式 phpunit.xml 即可,具体在运行测试阶段看如何指定配置
<?xml version=”1.0″ encoding=”UTF-8″?>

<phpunit
bootstrap=”../../bootstrap.php”
convertErrorsToExceptions=”true”
convertNoticesToExceptions=”false”
convertWarningsToExceptions=”false”
colors=”true”>
<filter>
<whitelist processuncoveredfilesfromwhitelist=”true”>
<directory suffix=”.php”>../../../app/Http/Controllers/Headline</directory>
<directory suffix=”.php”>../../../app/Http/Requests/Headline</directory>
<directory suffix=”.php”>../../../app/Models/Headline</directory>
<exclude><file>../../../app/Models/Headline/ArticleKeywordsRelationModel.php</file>
</exclude>
</whitelist>
</filter>

<testsuites>
<testsuite name=”Headline Test Suite”>
<directory>./</directory>
</testsuite>
</testsuites>

<php>
<ini name=”date.timezone” value=”PRC”/>
<env name=”APP_ENV” value=”DEV”/>
</php>

<logging>
<log type=”coverage-html” target=”logs/html/” lowUpperBound=”35″
highLowerBound=”70″/>
<log type=”json” target=”logs/logfile.json”/>
<log type=”tap” target=”logs/logfile.tap”/>
<log type=”junit” target=”logs/logfile.xml” logIncompleteSkipped=”false”/>
<log type=”testdox-html” target=”logs/testdox.html”/>
<log type=”testdox-text” target=”logs/testdox.txt”/>
</logging>

<listeners>
<!–<listener class=”\Test\Cases\Headline\Listeners\MyListener” file=”./Listeners/MyListener.php”>–>
<!–<arguments>–>
<!–<array>–>
<!–<element key=”0″>–>
<!–<string>Sebastian</string>–>
<!–</element>–>
<!–</array>–>
<!–<integer>22</integer>–>
<!–<string>April</string>–>
<!–<double>19.78</double>–>
<!–<null/>–>
<!–<object class=”stdClass”/>–>
<!–</arguments>–>
<!–</listener>–>
<!–<listener class=”\Test\Cases\Headline\Listeners\MyListener” file=”./Listeners/MyListener.php”>–>
<!–<arguments>–>
<!–<array>–>
<!–<element key=”0″>–>
<!–<string>Sebastian</string>–>
<!–</element>–>
<!–</array>–>
<!–<integer>22</integer>–>
<!–</arguments>–>
<!–</listener>–>
</listeners>
</phpunit>
测试用例案例
<?php
/**
* Created by PhpStorm.
* User: qikailin
* Date: 2019-01-29
* Time: 11:57
*/

namespace Test\Cases\Headline\Articles;

use App\Http\Controllers\Headline\ArticleController;
use App\Models\Headline\ArticleCategoryRelationModel;
use App\Models\Headline\ArticleContentModel;
use App\Models\Headline\ArticleKeywordsRelationModel;
use App\Models\Headline\ArticlesModel;
use Faker\Generator;
use Illuminate\Http\Request;
use Test\BaseCase;

class ArticleTest extends BaseCase
{
private static $model;

public static function setUpBeforeClass()
{
parent::setUpBeforeClass();

self::$model = new ArticlesModel();
}

/**
* 生成 factory faker 数据构建模型对象
* @codeCoverageIgnore
*/
public function factory($factory)
{
$words = [“ 测试 ”, “ 文章 ”, “ 模糊 ”, “ 搜索 ”];
$id = 262;
$factory->define(ArticlesModel::class, function (Generator $faker) use (&$id, $words) {
$id++;
return [
‘id’ => $id,
‘uri’ => $faker->lexify(‘T???????????????????’),
‘title’ => $id == 263 ? “ 搜索 ” : $words[rand(0, sizeof($words) – 1)],
‘authorId’ => 1,
‘state’ => 1,
‘isUpdated’ => 0,
];
});
}

/**
* 生成模拟的数据,需 seederRollback 成对出现
*/
public function seeder()
{
$articles = factory(ArticlesModel::class, 10)->make();
foreach ($articles as $article) {// 注意: article 为引用对象,不是 copy
if ($article->isRecommend) {
$article->recommendTime = time();
}
$article->save();
}
}

/**
* getArticleList 测试数据
* @return array
*/
public function getArticleListDataProvider()
{
return [
[1, “ 搜索 ”, 1, 10, 1],
[2, “ 搜索 ”, 1, 10, 0],
[2, null, 1, 10, 0],
[3, “ 搜索 ”, 1, 10, 0],
[1, null, 1, 10, 1],
[2, null, 1, 10, 0],
[3, null, 1, 10, 0],
];
}

/**
* @dataProvider getArticleListDataProvider
*/
public function testGetArticleList($type, $searchText, $page, $pageSize, $expceted)
{
$rst = self::$model->getArticleList($type, $searchText, $page, $pageSize);

$this->assertGreaterThanOrEqual($expceted, sizeof($rst));

$rst = self::$model->getArticleCount($type, $searchText);

$this->assertGreaterThanOrEqual($expceted, $rst);
}

/**
* addArticle 测试数据
* @return array
*/
public function addArticleDataProvider()
{
return [
[
[
‘id’ => 273,
‘uri’ => ‘dddddddddd0123’
],
‘save’,
0
],
[
[
‘id’ => 274,
‘uri’ => ‘dddddddddd123’
],
‘publish’,
0
],
[
[
‘id’ => 275,
‘uri’ => ‘dddddddddd456’
],
‘preview’,
0
],
];
}

/**
* @dataProvider addArticleDataProvider
*/
public function testAdd($data, $action, $expected)
{
$rst = self::$model->addArticle($data, $action);

if ($rst) {
self::$model::where(‘id’, $rst)->delete();
}

$this->assertGreaterThanOrEqual($expected, $rst);
}

public function testGetArticleInfo()
{
$rst = self::$model->getArticleInfo(263, 0);

$this->assertGreaterThanOrEqual(1, sizeof($rst));

$rst = self::$model->getArticleInfo(2000, 1);

$this->assertEquals(0, sizeof($rst));
}

/**
* 回滚模拟的数据到初始状态
*/
public function seederRollback()
{
self::$model::where(‘id’, ‘>=’, 263)->where(‘id’, ‘<=’, 272)->delete();
}
}
运行测试
cd {APPROOT}/tests/Cases/Headline
# mv phpunit-debug-custom.xml -> phpunit-debug.xml
../../../vendor/bin/phpunit –verbose -c phpunit-debug.xml
参考
PHPUnit 5.0 官方中文手册

正文完
 0