当前位置: 首页 > 编程日记 > 正文

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

本系列教程所有的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"?><phpunitbootstrap="../../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为引用对象,不是copyif ($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 官方中文手册

相关文章:

SLAM笔记(五)光束平差法(Bundle Adjustment)

1.什么是光束平差法 前边的八点法&#xff0c;五点法等可以求出闭式解的前提是已经知道确切的点对。但实际情况中往往存在大量的噪声&#xff0c;点与点不是精确地对应甚至出现一些错误匹配。 光束平差法由Bundle Adjustment翻译得来&#xff0c;有两层意思&#xff1a; 对场…

【Code forces】63B Settlers' Training

http://codeforces.com/problemset/problem/63/B 给你一串数字&#xff0c;直到所有数字都变为k为止&#xff0c;相同的数为一组&#xff0c;在一次中&#xff0c;所有不同的数都加1 1 2 2 3 → 2 2 3 4 → 2 3 4 4 → 3 4 4 4 → 4 4 4 4 #include <ios…

[elixir! #0007] [译] 理解Elixir中的宏——part.5 重塑AST by Saša Jurić

上一章我们提出了一个基本版的deftraceable宏&#xff0c;能让我们编写可跟踪的函数。宏的最终版本有一些剩余的问题&#xff0c;今天我们将解决其中的一个——参数模式匹配。 今天的练习表明我们必须仔细考虑宏可能接收到的输入。 问题 正如我上一次暗示的那样&#xff0c;当前…

vue-cli3环境变量与分环境打包

第一步 : 了解环境变量概念 我们可以根目录中的下列文件来指定环境变量: .env # 在所有的环境中被载入 .env.local # 在所有的环境中被载入&#xff0c;但会被 git 忽略 .env.[mode] # 只在指定的模式中被载入 .env.[mode].local # 只在指定…

SLAM闭合回环————视觉词典BOW小结

在目前实际的视觉SLAM中&#xff0c;闭环检测多采用DBOW2模型https://github.com/dorian3d/DBoW2&#xff0c;而bag of words 又运用了数据挖掘的K-means聚类算法&#xff0c;笔者只通过bag of words 模型用在图像处理中进行形象讲解&#xff0c;并没有涉及太多对SLAM的闭环检测…

【Codeforces】53D Physical Education (有点像冒泡)

http://codeforces.com/problemset/problem/53/D 从上面所给的序列变成下面所给的序列 交换的时候只能交换相邻的两个数字 输出每一步的交换方法&#xff0c;输出的是该元素在序列中的位置&#xff08;第一个数的位置是1&#xff09; 不要求输出步数最少的那一种方法 当同…

js脚本冷知识

js中有个很恶心的写法。比如这个var adsf(function(){})();这样的写法&#xff0c;主要是原生的&#xff0c;能在dom元素加载完之后实现自动加载这个脚本文件到里面去。可以验证这个&#xff08;function(){.......&#xff09;&#xff08;&#xff09;;在.......里面可以直接…

Python中is同一性运算符和==相等运算符区别

2019独角兽企业重金招聘Python工程师标准>>> 在区分is和这两种运算符区别之前&#xff0c;需要知道Python中对象包含的三个基本要素&#xff0c;分别是&#xff1a;id(身份标识)、type(数据类型)和value(值)。 比较对象的value(值) 是python标准操作符中的比较操作符…

C++实现十大排序算法(冒泡,选择,插入,归并,快速,堆,希尔,桶,计数,基数)排序算法时间复杂度、空间复杂度、稳定性比较(面试经验总结)

排序算法分类 内部排序算法又分为基于比较的排序算法和不基于比较的排序算法&#xff0c;其分类如下&#xff1a; 比较排序&#xff1a; 直接插入排序 希尔排序 &#xff08;插入&#xff09; 冒泡排序 快速排序 (交换) 直接选择排序 堆排序&#xff08;选择&#…

32位处理器是什么意思

问题描述&#xff1a;朋友那个32位处理器&#xff0c;2的32次方算出来的单位是不是应该是4294967296位&#xff08;bit&#xff09;吧&#xff0c;怎么就成字节了呢&#xff1f;单位错了&#xff0c;那个32位是指32位地址总线&#xff0c;而不是32位数据。地址的数量单位是个&a…

【Codeforces】913C Party Lemonade (贪...)。

http://codeforces.com/contest/913/problem/C 这个题和以前见过的有点不一样&#xff0c;可以重复选择&#xff0c;这个有点emmm 首先将a数组优化&#xff0c;举个例子&#xff0c;如果1L20元&#xff0c;2L50元&#xff0c;那么将a[1]赋值为40&#xff0c;而不是50。 之后…

GDB 调试 Mysql 实战(二)GDB 调试打印

背景 在 https://mengkang.net/1328.html 实验中&#xff0c;我们通过optimizer_trace发现group by会使用intermediate_tmp_table&#xff0c;而且里面的的row_length是20&#xff0c;抱着"打破砂锅问到底"的求学精神&#xff0c;所以想通过 gdb 调试源码的方式看这个…

移动端AR的适用分析(二)

移动端AR的适用分析&#xff08;二&#xff09;1. 单目SLAM难点 2. 视觉SLAM难点 3. 可能的解决思路 单目slam的障碍来自于理论和实践两个方面。理论障碍可以看做是固有的&#xff0c;无法通过硬件选型或软件算法来解决的&#xff0c;例如单目初始化和尺度问题。实践问题包括计…

新的理念、 新的解决方案、 新的Azure Stack技术预览

Jeffrey Snover 我们很高兴地宣布︰ Azure Stack Technical Preview 2&#xff08;TP2&#xff09;已发布&#xff01;我们朝着向您的数据中心提供Azure服务能力的目标又更近一步。自发布第一个技术预览版&#xff08;TP1&#xff09;以来&#xff0c;我们访问了很多用户&…

【HDU】1084 What Is Your Grade? (结构体 sort)

http://acm.hdu.edu.cn/showproblem.php?pid1084 题目的关键&#xff1a; 1、Note, only 1 student will get the score 95 when 3 students have solved 4 problems. If you can solve 4 problems, you can also get a high score 95 or 90 (you can get the former(前者)…

FastDFS之Linux下搭建

1.软件环境 CentOS6.5 FastDFS v5.05 libfastcommon- - master.zip&#xff08;是从 FastDFS 和 FastDHT 中提取出来的公共 C 函数库&#xff09; fastdfs- - nginx- - module_v1.16.tar.gz nginx- - 1.6.2.tar.gz fastdfs_client_java._v1.25.tar.gz 2.FastDFS集群规划 描述 …

Linux进程与线程的区别 详细总结(面试经验总结)

首先&#xff0c;简要了解一下进程和线程。对于操作系统而言&#xff0c;进程是核心之核心&#xff0c;整个现代操作系统的根本&#xff0c;就是以进程为单位在执行任务。系统的管理架构也是基于进程层面的。在按下电源键之后&#xff0c;计算机就开始了复杂的启动过程&#xf…

【HDU/POJ/ZOJ】Calling Extraterrestrial Intelligence Again (素数打表模板)

http://poj.org/problem?id1411 POJ http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode1689 ZOJ http://acm.hdu.edu.cn/showproblem.php?pid1239 HDU 都是同一个题&#xff0c;但是可能你在HDU上AC&#xff0c;在POJ和ZOJ上是TLE&#xff08;所以还有待…

[AVR]使用AVR单片机驱动舵机

最近参加了三系举办的小车比赛(好像叫什么"驭远杯")。领导要求我驱动3-4个舵机。研究了几日&#xff0c;总算折腾出一个方案..、 1.舵机驱动的基本原理 &#xff08;可以参考http://blog.sina.com.cn/s/blog_8240cbef01018hu1.html&#xff09; "控制信号由接收…

高阶函数的使用

问题 字节跳动面试时问题&#xff1a;原函数例如fetchData是一个异步函数&#xff0c;尝试从服务器端获取一些信息并返回一个Promise。写一个新的函数可以自动重试一定次数&#xff0c;并且在使用上和原函数没有区别。 思路 这个问题其实不是很难&#xff0c;不过可能是太菜了紧…

内存溢出和内存泄漏的定义,产生原因以及解决方法(面试经验总结)

一、定义&#xff08;概念与区别&#xff09; 内存溢出 out of memory&#xff0c;是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用&#xff0c;出现out of memory&#xff1b;比如申请 了一个integer,但给它存了long才能存下的数&#xff0c;那就是内存溢出。 …

【Codeforces】716B Complete the Word (26个字母)

http://codeforces.com/contest/716/problem/B 给你一个字符串该字符串中是否存在长度为26且这26个字母没有重复 一个满足上述条件但是部分区域是问号的话&#xff0c;需要用剩下的字母覆盖掉问号&#xff0c;其余部分的问号可以随便赋值 没有的话输出-1 暴力即可。 #incl…

MySQL ERROR 1878 解决办法

MySQL ERROR 1878报错解决办法错误重现Part1:大表修改字段mysql> ALTER TABLE erp-> ADD COLUMN eas_status tinyint(3) unsigned NOT NULL DEFAULT 0 AFTER totalprice;ERROR 1878 (HY000): Temporary file write failure.mysql> \q这里可以看到&#xff0c;添加字…

共享程序集和强命名程序集(3):强命名程序集的一些作用

强命名程序集能防篡改 用私钥对程序集进行签名&#xff0c;并将公钥和签名嵌入程序集&#xff0c;CLR就可以炎症程序集未被修改或破坏。程序集安装到GAC时&#xff0c;系统对包含清单的那个文件的内容进行哈希处理&#xff0c;将Hash值与PE文件中嵌入的RSA数字签名进行比较。如…

堆和栈的区别(面试经验总结)

C中&#xff0c;内存分为5个区&#xff1a;堆、栈、自由存储区、全局/静态存储区和常量存储区。 栈&#xff1a;是由编译器在需要时自动分配&#xff0c;不需要时自动清除的变量存储区。通常存放局部变量、函数参数等。 堆&#xff1a;是由new分配的内存块&#xff0c;由程序员…

百度Q3财报里的“大生意”

在今日发布的Q3财报中&#xff0c;百度花了不少篇幅来介绍人工智能业务的进展&#xff0c;作为百度的技术核心&#xff0c;近段时间几乎所有百度业务都在与人工智能做深入结合&#xff0c;这预示着移动互联网信息化技术发展已经全面开启人工智能时代&#xff0c;而百度势必要在…

【Codeforces/HDU】76A Plus and xor / 2095 find your present (2)(异或)。

http://codeforces.com/contest/76/problem/D A X Y B X xor Y 异或&#xff08;不进位加法&#xff09;&#xff1a;两个二进制数&#xff0c;对应的位置上&#xff0c;相同为0&#xff0c;不同为1 性质&#xff1a;a^a0&#xff0c;a^0a&#xff0c;满足交换律 所以…

前端项目如何管理

前端项目如何管理 前端项目的管理分为两个维度&#xff1a;项目内的管理与多项目之间的管理。 1. 项目内的管理 在一个项目内&#xff0c;当有多个开发者一起协作开发时&#xff0c;或者功能越来越多、项目越来越庞大时&#xff0c;保证项目井然有序的进行是相当重要的。 一般会…

CMake学习(一)

什么是 CMake 你或许听过好几种 Make 工具&#xff0c;例如 GNU Make &#xff0c;QT 的 qmake &#xff0c;微软的 MS nmake&#xff0c;BSD Make&#xff08;pmake&#xff09;&#xff0c;Makepp&#xff0c;等等。这些 Make 工具遵循着不同的规范和标准&#xff0c;所执行…

【Codeforces】1104C Grid game (变异的俄罗斯方块)

http://codeforces.com/problemset/problem/1104/C 4 X 4 的方格 放置 1*2的矩形&#xff08;用1表示&#xff09;和2*1的矩形&#xff08;用0表示&#xff09; 只要有一行或者一列都填满了&#xff0c;就会自动消除&#xff0c;就可以放心的矩形了&#xff0c;只要不重叠就可…