PHP 自动化测试
Laravel 8 框架使用 PHPUnit 9 作为默认的单元测试组件,适用于 PHP 7.3 及更高版本。
PHPUnit 可生成 HTML、Clover 等格式的测试覆盖率报告,JUnit 等格式的测试结果,但不支持检查测试覆盖率大小,可通过 「phpunit-coverage-check」进行检查。
安装
在代码目录执行命令进行安装:
$ composer require --dev phpunit/phpunit
$ composer require --dev rregeer/phpunit-coverage-check
编写测试代码
按照 Laravel 官方文档,编写测试代码。
业务代码(app/Http/Controllers/Welcome.php
):
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class Welcome extends Controller
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
return view('welcome');
}
}
路由(routes/web.php
):
use Illuminate\Support\Facades\Route;
Route::get('/', \App\Http\Controllers\Welcome::class);
测试代码(app/Http/Controllers/Welcome.php
):
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
本地运行
本地运行测试,将会生成「JUnit 格式测试结果」和「HTML 和 Clover 格式覆盖率报告」。
$ ./vendor/bin/phpunit --coverage-html storage/reports/tests/ --coverage-clover storage/reports/tests/clover.xml --log-junit storage/test-results/junit.xml tests/
$ head storage/test-results/junit.xml
$ open storage/reports/tests/index.html
根据 Clover 报告检查测试覆盖率,如果不达标,将会报错退出:
$ ./vendor/bin/coverage-check storage/reports/tests/clover.xml 80
Total code coverage is 53.85 % which is below the accepted 80%
$ echo $?
1
持续集成
带数据库的测试
自动化测试需要临时的基础设施(比如 MySQL、Redis、Elasticsearch),无需购买,启动多个 Docker 后台即可,测试完毕自动删除。
node {
stage("检出") {
checkout([
$class: 'GitSCM',
branches: [[name: GIT_BUILD_REF]],
userRemoteConfigs: [[
url: GIT_REPO_URL,
credentialsId: CREDENTIALS_ID
]]])
}
stage('准备数据库') {
sh 'docker network create bridge1'
sh(script:'docker run --net bridge1 --name mysql -d -e "MYSQL_ROOT_PASSWORD=my-secret-pw" -e "MYSQL_DATABASE=test_db" mysql:5.7', returnStdout: true)
sh(script:'docker run --net bridge1 --name redis -d redis:5', returnStdout: true)
}
docker.image('ecoding/php:8.0').inside("--net bridge1 -v \"${env.WORKSPACE}:/root/code\" -e 'APP_ENV=testing' -e 'DB_DATABASE=test_db'" +
" -e 'DB_USERNAME=root' -e 'DB_PASSWORD=my-secret-pw' -e 'DB_HOST=mysql' -e 'REDIS_HOST=redis'" +
" -e 'APP_KEY=base64:tbgOBtYci7i7cdx5RiFE3KZzUkRtJfbU3lbj5uPdL8U='") {
sh 'composer install'
stage('单元测试') {
sh 'XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-html storage/reports/tests/ --log-junit storage/test-results/junit.xml --coverage-text tests/'
junit 'storage/test-results/junit.xml'
codingHtmlReport(name: '测试覆盖率报告', path: 'storage/reports/tests/')
}
stage('生成 API 文档') {
sh 'php artisan l5-swagger:generate'
codingReleaseApiDoc(apiDocId: '1', apiDocType: 'specificFile', resultFile: 'storage/api-docs/api-docs.json')
}
}
CODING_DOCKER_REG_HOST = "${CCI_CURRENT_TEAM}-docker.pkg.${CCI_CURRENT_DOMAIN}"
CODING_DOCKER_IMAGE_NAME = "${env.PROJECT_NAME.toLowerCase()}/laravel-docker/laravel-demo"
stage('构建 Docker 镜像') {
if (env.TAG_NAME ==~ /.*/ ) {
DOCKER_IMAGE_VERSION = "${env.TAG_NAME}"
} else if (env.MR_SOURCE_BRANCH ==~ /.*/ ) {
DOCKER_IMAGE_VERSION = "mr-${env.MR_RESOURCE_ID}-${env.GIT_COMMIT_SHORT}"
} else {
DOCKER_IMAGE_VERSION = "${env.BRANCH_NAME.replace('/', '-')}-${env.GIT_COMMIT_SHORT}"
}
// 本项目内的制品库已内置环境变量 CODING_ARTIFACTS_CREDENTIALS_ID,无需手动设置
docker.withRegistry("https://${env.CCI_CURRENT_TEAM}-docker.pkg.coding.net", "${env.CODING_ARTIFACTS_CREDENTIALS_ID}") {
docker.build("${CODING_DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}").push()
}
}
}
不带数据库的测试
pipeline {
agent {
docker {
image 'ecoding/php:8.0'
reuseNode 'true'
args '-v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker'
}
}
stages {
stage("检出") {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: GIT_BUILD_REF]],
userRemoteConfigs: [[
url: GIT_REPO_URL,
credentialsId: CREDENTIALS_ID
]]])
}
}
stage('准备依赖') {
steps {
sh 'composer install'
}
}
stage('单元测试') {
post {
always {
junit 'storage/test-results/junit.xml'
}
success {
codingHtmlReport(name: '测试覆盖率报告', path: 'storage/reports/tests/')
}
}
steps {
sh 'touch database/database.sqlite'
sh 'XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-html storage/reports/tests/ --log-junit storage/test-results/junit.xml --coverage-text tests/'
}
}
stage('生成 API 文档') {
steps {
sh 'php artisan l5-swagger:generate'
codingReleaseApiDoc(apiDocId: '1', apiDocType: 'specificFile', resultFile: 'storage/api-docs/api-docs.json')
}
}
stage('构建 Docker 镜像') {
steps {
script {
if (env.TAG_NAME ==~ /.*/ ) {
DOCKER_IMAGE_VERSION = "${env.TAG_NAME}"
} else if (env.MR_SOURCE_BRANCH ==~ /.*/ ) {
DOCKER_IMAGE_VERSION = "mr-${env.MR_RESOURCE_ID}-${env.GIT_COMMIT_SHORT}"
} else {
DOCKER_IMAGE_VERSION = "${env.BRANCH_NAME.replace('/', '-')}-${env.GIT_COMMIT_SHORT}"
}
// 本项目内的制品库已内置环境变量 CODING_ARTIFACTS_CREDENTIALS_ID,无需手动设置
docker.withRegistry("https://${env.CCI_CURRENT_TEAM}-docker.pkg.coding.net", "${env.CODING_ARTIFACTS_CREDENTIALS_ID}") {
docker.build("${CODING_DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}").push()
}
}
}
}
}
environment {
CODING_DOCKER_REG_HOST = "${CCI_CURRENT_TEAM}-docker.pkg.${CCI_CURRENT_DOMAIN}"
CODING_DOCKER_IMAGE_NAME = "${env.PROJECT_NAME.toLowerCase()}/laravel-docker/laravel-demo"
}
}
运行结果
输出测试报告:
输出通用报告:
报告详情:
问题反馈 >
2022-05-07最近更新
感谢反馈有用
感谢反馈没用
在阅读中是否遇到以下问题?*
您希望我们如何改进?*
如果您希望得到回复,请留下您的邮箱地址。