PHPUnit实践二(生命周期)

本系列教程所有的PHPUnit测试基于PHPUnit6.5.9版本,Lumen 5.5框架

PHPUnit测试一个文件类的生命周期

PHPUnit实践二(生命周期)

理解PHPUnit加载机制(Lumen版)

  1. PHPUnit自动测试文件会自动加载引入(include file)
  2. PHPUnit去启动setUp方法,Lumen里重写了setUp,加载了bootstrap/app.php
  3. app.php加载了composer的autoload,借此你项目所有自动加载环境都有了,不过不包含tests目录

至此我们引入了我们需要构建自己的自动加载类

增加tests的自动加载

我们需要给tests下的测试用例创建类似下面的结构

├── BaseCase.php 重写过Lumen基类的测试基类,用于我们用这个基类做测试基类,后续会说明
├── bootstrap.php tests自动加载文件
├── Cases 测试用例目录
│   └── Demo 测试模块
│       ├── logs 日志输出目录
│       ├── PipeTest.php PHPUnit流程测试用例
│       ├── phpunit.xml phpunit配置文件xml
│       └── README.md 本模块测试用例说明
├── ExampleTest.php 最原始测试demo
└── TestCase.php Lumen自带的测试基类

tests自动加载文件代码

<?php
/**
 * 测试框架的自动加载测试文件类
 * User: qikailin
 */
error_reporting(E_ALL ^ E_NOTICE);
require __DIR__ . '/../vendor/autoload.php';

define('MY_TESTS_DIR_BASE', realpath(dirname(__FILE__)));

set_include_path(implode(PATH_SEPARATOR, array(
    WPT_TEST_DIR_BASE,
    get_include_path()
)));

spl_autoload_register(function ($class) {

    $classFile = MY_TESTS_DIR_BASE . DIRECTORY_SEPARATOR . str_replace(["Test\\", "/", "\\"],
            ["", DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $class) . ".php";
    
    if (file_exists($classFile)) {
        include_once $classFile;
    }

}, true, false);

phpunit.xml

自动加载配置bootstrap文件

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
        bootstrap="../../bootstrap.php"
        convertErrorsToExceptions="true"
        convertNoticesToExceptions="false"
        convertWarningsToExceptions="false"
        colors="true">
</phpunit>

流程测试代码

TestCase.php

# 代码头部添加 命令空间Test
namespace Test;

PipeTest 流程代码

<?php
/**
 * 测试类的每个测试方法都会运行一次 setUp() 和 tearDown() 模板方法(同时,每个测试方法都是在一个全新的测试类实例上运行的)。
 * 另外,setUpBeforeClass() 与 tearDownAfterClass() 模板方法将分别在测试用例类的第一个测试运行之前和测试用例类的最后一个测试运行之后调用。
 * 如果有需要共享的对象或变量,可以放在setUpBeforeClass,并设置为静态属性
 * User: qikailin
 */
namespace Test\Cases\Demo;

use Test\BaseCase;

class PipeTest extends BaseCase
{
    public static function setUpBeforeClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function setUp()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    /**
     * 测试方法的前置执行,setUp之后
     */
    protected function assertPreConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function testOne()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(true);
    }

    public function testTwo()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        // 两个交换下顺序可以看下效果
        // 正常执行成功assert可以继续执行,失败的会跳出方法
        $this->assertArrayHasKey('d', ['d'=>1, 'e'=>2]);
        $this->assertTrue(false);
    }

    public function testThree()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(false);
    }

    public function testFour()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    /**
     * 测试方法成功后的后置执行,tearDown之前
     */
    protected function assertPostConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function tearDown()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public static function tearDownAfterClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    /**
     * 不成功后拦截方法
     * 必须重新抛出错误,如果不抛出错误,断言会当成成功了
     */
    public function onNotSuccessfulTest(\Throwable $e)
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        // 必须重新抛出错误,如果不抛出错误,断言会当成成功了
        throw $e;
    }
}

运行

# 你可以把vendor/bin加入到环境变量PATH
cd tests/Demo
../../../vendor/bin/phpunit

运行输出

PHPUnit 6.5.9 by Sebastian Bergmann and contributors.

Test\Cases\Demo\PipeTest::setUpBeforeClass
Test\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testOne
Test\Cases\Demo\PipeTest::assertPostConditions
Test\Cases\Demo\PipeTest::tearDown
.Test\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testTwo
Test\Cases\Demo\PipeTest::tearDown
Test\Cases\Demo\PipeTest::onNotSuccessfulTest
FTest\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testThree
Test\Cases\Demo\PipeTest::tearDown
Test\Cases\Demo\PipeTest::onNotSuccessfulTest
FTest\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testFour
Test\Cases\Demo\PipeTest::assertPostConditions
Test\Cases\Demo\PipeTest::tearDown
R                                                                4 / 4 (100%)Test\Cases\Demo\PipeTest::tearDownAfterClass


Time: 1.29 seconds, Memory: 6.00MB

There were 2 failures:

1) Test\Cases\Demo\PipeTest::testTwo
Failed asserting that false is true.

/xxx/tests/Cases/Demo/PipeTest.php:47

2) Test\Cases\Demo\PipeTest::testThree
Failed asserting that false is true.

/xxx/tests/Cases/Demo/PipeTest.php:53

--

There was 1 risky test:

1) Test\Cases\Demo\PipeTest::testFour
This test did not perform any assertions

FAILURES!
Tests: 4, Assertions: 4, Failures: 2, Risky: 1.

Generating code coverage report in HTML format ... done

整理流程输出

Test\Cases\Demo\PipeTest::setUpBeforeClass
Test\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testOne
Test\Cases\Demo\PipeTest::assertPostConditions
Test\Cases\Demo\PipeTest::tearDown
Test\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testTwo
Test\Cases\Demo\PipeTest::tearDown
Test\Cases\Demo\PipeTest::onNotSuccessfulTest
Test\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testThree
Test\Cases\Demo\PipeTest::tearDown
Test\Cases\Demo\PipeTest::onNotSuccessfulTest
Test\Cases\Demo\PipeTest::setUp
Test\Cases\Demo\PipeTest::assertPreConditions
Test\Cases\Demo\PipeTest::testFour
Test\Cases\Demo\PipeTest::assertPostConditions
Test\Cases\Demo\PipeTest::tearDown
Test\Cases\Demo\PipeTest::tearDownAfterClass

总结

一个测试类文件,从setUpBeforeClass加载,且仅此加载一次
每个测试方法都会走的过程:setUp->assertPreConditions->测试方法->[assert成功执行:assertPostConditions]->tearDown->[assert执行失败:onNotSuccessfulTest,且本方法需要抛出错误]
本个测试类文件执行tearDownAfterClass结束

参考

PHPUnit 6.5 官方文档

相关推荐