本系列教程所有的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<?phpnamespace 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 官方中文手册
...