[ Laravel从入门到精通 ] 测试系列 —— 在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇

栏目: 编程工具 · 发布时间: 6年前

内容简介:介绍完 PHPUnit 的基本使用和 Laravel 框架自带的编排文件其中包含了一行最基本的断言测试,用于判断指定的参数是否为真,并且这个测试永远是通过的。Laravel 的单元测试其实是原原本本继承了 PHPUnit 的单元测试功能,这里的父类下面我们通过几个常见的场景介绍下如何基于 PHPUnit 编写单元测试。

简介

介绍完 PHPUnit 的基本使用和 Laravel 框架自带的编排文件 phpunit.xml 文件,今天开始我们正式准备在 Laravel 项目中基于 PHPUnit 编写单元测试和功能测试,通过上篇教程介绍的编排文件我们知道,Laravel 的单元测试用例位于 tests/Unit 目录下,框架本身也为我们提供了一个示例测试文件 ExampleTest.php

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $this->assertTrue(true);
    }
}

其中包含了一行最基本的断言测试,用于判断指定的参数是否为真,并且这个测试永远是通过的。Laravel 的单元测试其实是原原本本继承了 PHPUnit 的单元测试功能,这里的父类 Tests\TestCase 从根源上继承自 PHPUnit\Framework\TestCase ,所以我们可以在测试用例中使用所有 PHPUnit 支持的 断言方法测试注解

下面我们通过几个常见的场景介绍下如何基于 PHPUnit 编写单元测试。

对变量进行测试

PHPUnit 底层提供了很多断言方法用于对变量进行测试,这些变量通常是业务代码类方法或函数的返回值,我们在 Unit\ExampleTest 中新增一个 testVariables 方法:

public function testVariables()
{
    $bool = false;
    $number = 100;
    $arr = ['Laravel', 'PHP', '学院君'];
    $obj = null;

    // 断言变量值是否为假,与 assertTrue 相对
    $this->assertFalse($bool);
    // 断言给定变量值是否与期望值相等,与 assertNotEquals 相对
    $this->assertEquals(100, $number);
    // 断言数组中是否包含给定值,与 assertNotContains 相对
    $this->assertContains('学院君', $arr);
    // 断言数组长度是否与期望值相等,与 assertNotCount 相对
    $this->assertCount(3, $arr);
    // 断言数组是否不会空,与 assertEmpty 相对
    $this->assertNotEmpty($arr);
    // 断言变量是否为 null,与 assertNotNull 相对
    $this->assertNull($obj);
}

相应的断言用途在注释中已经说明了,我们可以对各种类型的变量从各种维度进行断言,甚至还可以对文件、目录、正则表达式进行断言,并且很多断言都可以从正反两个方法进行,相关的调用都很简单,你可以在需要的时候查看官方文档选择相应的断言方法: https://phpunit.readthedocs.io/zh_CN/latest/assertions.html

运行上面的测试用例,结果如下,每个测试方法都代表一个测试用例,所以上面的单元测试包含两个测试用例,7个断言:

[ Laravel从入门到精通 ] 测试系列 —— 在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇

对输出进行测试

除了对变量进行测试外,还可以对页面输出进行测试,这可以通过 PHPUnit 提供的 expectOutputString 方法来实现:

public function testOutput()
{
    $this->expectOutputString('学院君');
    echo '学院君';
}

此外还可以对输出数据进行正则判断:

public function testOutputRegex()
{
    $this->expectOutputRegex('/Laravel/i');
    echo 'Laravel学院';
}

上述测试结果都是通过的:

[ Laravel从入门到精通 ] 测试系列 —— 在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇

对异常进行测试

类似的,还可以通过 expectException 方法对异常进行测试,为了让测试用例更加符合真实场景,我们在 app 目录下新增一个 Services 子目录,然后在该子目录下创建一个 TestService 类并初始化代码如下:

<?php
namespace App\Services;

class TestService
{
    public function invalidArgument()
    {
        throw new \InvalidArgumentException('无效的参数');
    }
}

然后回到 Unit\ExampleTest ,编写一个新的测试用例如下:

public function testException()
{
    $this->expectException(\InvalidArgumentException::class);
    $service = new TestService();
    $service->invalidArgument();
}

运行测试用例,结果为通过:

[ Laravel从入门到精通 ] 测试系列 —— 在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇

如果传入的抛出异常类的是父类,也会通过:

$this->expectException(\Exception::class);

除此之外,还可以进一步对异常明细进行测试,比如通过 expectExceptionCode()expectExceptionMessage()expectExceptionMessageRegExp() 方法可以用于测试异常码、异常信息。

除了通过上述方法,还可以通过注解对异常进行测试,这种方式更加方便:

/**
 * @expectedException \InvalidArgumentException
 */
public function testExceptionAnnotation()
{
    $this->service->invalidArgument();
}

由于两个测试用例中都用到了 TestService ,所以我们将其在 setUp 方法进行初始化:

/**
 * @var TestService
 */
protected $service;

protected function setUp(): void
{
    parent::setUp();
    $this->service = new TestService();
}

上面两个测试用例从功能上说是等价的:

[ Laravel从入门到精通 ] 测试系列 —— 在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇

对错误进行测试

默认情况下,PHPUnit 会将 PHP 错误、警告和通知都转化为异常,在上一篇 PHP 编排文件 phpunit.xml 中我们提到,Laravel 也默认配置为做这些转化,所以我们可以通过测试异常的方式对业务代码中的错误进行测试。具体用法和异常测试一样,就不再赘述了。

测试的依赖关系

有的时候,我们需要测试的两个用例之间可能有依赖关系,比如我们在 TestService 定义如下个方法:

protected $stack = [];

public function init()
{
    $this->stack = ['学院君', 'Laravel学院', '单元测试'];
}

public function stackContains($value)
{
    return in_array($value, $this->stack);
}

public function getStackSize()
{
    return count($this->stack);
}

我们在测试 stackContains 方法时,往往要先调用 init 方法,但是在单元测试中,每个方法都有独立的测试用例,如果多次调用有可能会对数据造成污染,那我们能否在 init 方法测试用例的运行基础上运行 stackContains 方法的测试用例呢?这个时候,我们说这两个测试用例之间是具有依赖关系的,PHPUnit 中通过 @depends 注解对这种依赖关系进行了支持,我们可以在 Unit\ExampleTest 中编写测试用例如下:

public function testInitStack()
{
    $this->service->init();
    $this->assertEquals(3, $this->service->getStackSize());

    return $this->service;
}

/**
 * @depends testInitStack
 * @param TestService $service
 */
public function testStackContains(TestService $service)
{
    $contains = $service->stackContains('学院君');
    $this->assertTrue($contains);
}

testStackContains 用例中,我们将 testInitStack 用例返回的 $service 实例传递进来,并在此基础上进行测试。

数据提供器

除了支持测试用例之间的依赖之外,PHPUnit 还可以通过 @dataProvider 注解为多个测试用例提供初始化数据:

public function initDataProvider()
{
    return [
        ['学院君'],
        ['Laravel学院']
    ];
}

/**
 * @depends testInitStack
 * @dataProvider initDataProvider
 */
public function testIsStackContains()
{
    $arguments = func_get_args();
    $service = $arguments[1];
    $value = $arguments[0];
    $this->assertTrue($service->stackContains($value));
}

在这个测试用例中,我们通过 initDataProvider 方法作为数据提供器,数据供给器方法必须声明为 public ,其返回值要么是一个数组,其每个元素也是数组;要么是一个实现了 Iterator 接口的对象,在对它进行迭代时每步产生一个数组。每个数组都是测试数据集的一部分,将以它的内容作为参数来调用测试方法。

然后我们在需要用到这个数据提供器的测试用例上用 @dataProvider 注解进行声明,在这个示例中我们迭代数据提供器数组,将其中的数据作为参数传入 TestServicestackContains 方法以判断对应值在 stack 属性中是否存在。

以下是上述用例运行结果:

[ Laravel从入门到精通 ] 测试系列 —— 在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇

关于 Laravel 基于 PHPUnit 编写单元测试用例我们就简单介绍到这里,在这篇教程中我们基本上使用的都是 PHPUnit 提供的原生测试功能,下一篇我们将围绕 Laravel 功能测试展开,主要是对控制器和 API 接口的测试,在那里,我们将开始就 Laravel 框架在 PHPUnit 基础上封装的新功能新特性进行介绍。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Master Switch

The Master Switch

Tim Wu / Knopf / 2010-11-2 / USD 27.95

In this age of an open Internet, it is easy to forget that every American information industry, beginning with the telephone, has eventually been taken captive by some ruthless monopoly or cartel. Wit......一起来看看 《The Master Switch》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

html转js在线工具
html转js在线工具

html转js在线工具