Laravel的单元测试

思考并回答以下问题:

PHPUnit

Laravel的单元测试

创建一个新的测试

使用 PHPUnit 的第一步是创建一个新的测试类。测试类的约定是它们存储在应用程序目录的 ./tests/ 下。在这个文件夹中,每个测试类都被命名为 Test.php 。这种格式允许 PHPUnit 查找每个测试类—-它将忽略任何不以 Test.php 结尾的文件。

在新的 Laravel 应用程序中,你会注意到 ./tests/ 目录中有两个文件: ExampleTest.php 和 TestCase.php. TestCase.php 文件是一个引导文件用于在我们的测试中设置 Laravel 环境。这允许我们在测试中使用 Laravel Facades 并为测试助手提供框架,我们将在稍后介绍。 ExampleTest.php 是一个示例测试类,其中包含使用应用程序测试助手的基本测试用例-暂时忽略它。

要创建一个新的测试类,我们可以手动创建一个新文件,或者运行由 Laravel 提供的 Artisan 命令 make:test

为了创建一个名为 BasicTest 的测试类,我们只需要运行这个 artisan 命令:

php artisan make:test BasicTest

Laravel 将创建一个如下所示的基本测试类:

<?php
class BasicTest extends TestCase
{
/**

* 一个基本的测试示例。
*
* @return void
*/
public function testExample()
{
    $this->assertTrue(true);
}

}

这里要注意的最重要的事情是 test 方法名称上的前缀,与 Test 类名后缀一样,这样 test 前缀告诉 PHPUnit 在测试时运行哪些方法。如果您忘记了 test 前缀,那么 PHPUnit 将忽略该方法。

在我们第一次运行测试套件之前,有必要指出 Laravel 提供的默认 phpunit.xml 文件。 PHPUnit 在运行时会自动在当前目录中查找名为 phpunit.xml 或者 phpunit.xml.dist 的文件。您可以在此处配置测试的特定选项。

这个文件中有很多信息,但是现在最重要的部分是在 testsuite 目录定义:

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

./tests/ ...

这将告诉 PHPUnit 运行时在 ./tests/ 目录中找到的测试,正如我们之前所知,这是存储测试的约定。

现在我们已经创建了一个基本测试,并且知道了 PHPUnit 配置,现在是第一次运行测试的时候了。

您可以通过运行以下 phpunit 命令来运行测试:

./vendor/bin/phpunit

您应该看到与此类似的输出:

PHPUnit 4.8.19 by Sebastian Bergmann and contributors.

..

Time: 103 ms, Memory: 12.75Mb

OK (2 tests, 3 assertions)

现在我们已经有了一个有效的 PHPUnit 设置,现在是时候开始编写一个基本测试了。

注意,它会统计2个测试和3个断言,因为 ExampleTest.php 文件包含了一个带有两个断言的测试。我们的新基本测试包括一个单独的断言,该断言已通过。

写一个基础测试
为了帮助 PHPUnit 提供的基本断言,我们将首先创建一个提供一些简单功能的基本类

在 ./app/ 目录中创建一个名为 Box.php 的新文件,并复制此示例类:

<?php
namespace App;

class Box
{
/**

* @var array
*/
protected $items = [];

/**
* 使用给定项构造框
*
* @param array $items
*/
public function __construct($items = [])
{
    $this->items = $items;
}

/**
* 检查指定的项目是否在框中。
*
* @param string $item
* @return bool
*/
public function has($item)
{
    return in_array($item, $this->items);
}

/**
* 从框中移除项,如果框为空,则为 null 。
*
* @return string
*/
public function takeOne()
{
    return array_shift($this->items);
}

/**
* 从包含指定字母开头的框中检索所有项目。
*
* @param string $letter
* @return array
*/
public function startsWith($letter)
{
    return array_filter($this->items, function ($item) use ($letter) {
        return stripos($item, $letter) === 0;
    });
}

}

接下来, 打开你的 ./tests/BasicTest.php 类(我们之前创建的类),并删除默认创建的 testExample 方法, 你应该留一个空类。

我们现在将使用七个基本的 PHPUnit 断言来为我们的 Box 类编写测试。这些断言是:

assertTrue()
assertFalse()
assertEquals()
assertNull()
assertContains()
assertCount()
assertEmpty()
assertTrue() 和 assertFalse()
assertTrue() 和 assertFalse() 允许你声明一个值等于 true 或 false 。这意味着它们非常适合测试返回布尔值的方法。在我们的 Box 类中,我们有一个名为 has($item) 的方法,当指定的项在 box 中或不在 box 中时,该方法返回对应返回 true 或 false .

要在 PHPUnit 中为此编写测试,我们可以执行以下操作:

<?php
use App\Box;

class BasicTest extends TestCase
{
public function testHasItemInBox()
{
$box = new Box([‘cat’, ‘toy’, ‘torch’]);

    $this->assertTrue($box->has('toy'));
    $this->assertFalse($box->has('ball'));
}

}

注意我们如何只将一个参数传递给 assertTrue() 和 assertFalse() 方法,并且它是 has($item) 方法的输入.

如果您现在运行 ./vendor/bin/phpunit 命令,您会注意到输出包括:

OK (2 tests, 4 assertions)

这意味着我们的测试已经通过。

如果您将 assertFalse() 替换成 assertTrue() 并运行 phpunit 命令,输出将如下所示:

PHPUnit 4.8.19 by Sebastian Bergmann and contributors.

F.

Time: 93 ms, Memory: 13.00Mb

There was 1 failure:

1) BasicTest::testHasItemInBox
Failed asserting that false is true.

./tests/BasicTest.php:12

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

这告诉我们第12行的断言未能断言 false 值是 true - 因为我们将 assertFalse() 替换为 assertTrue() 。

将其交换回来,然后重新运行 PHPUnit 。测试应该再次通过,因为我们已经修复了破损的测试。

assertEquals() 与 assertNull()
接下来,让我们看看 assertEquals(), 以及 assertNull()。

assertEquals() 用于比较变量实际值与预期值是否相等。我们用它来检查 takeOne() 方法的返回值是否为 Box 内的当前值。当 Box 为空时,takeOne() 将返回 null,我们亦可使用 assertNull() 来进行检查。

与 assertTrue()、assertFalse() 以及 assertNull() 不同,assertEquals() 需要两个参数。第一个参数为 预期 值,第二个参数则为 实际 值。

可参照如下代码实现以上断言(assertions):

<?php
use App\Box;

class BasicTest extends TestCase
{
public function testHasItemInBox()
{
$box = new Box([‘cat’, ‘toy’, ‘torch’]);

    $this->assertTrue($box->has('toy'));
    $this->assertFalse($box->has('ball'));
}

public function testTakeOneFromTheBox()
{
    $box = new Box(['torch']);

    $this->assertEquals('torch', $box->takeOne());

    // 当前 Box 为空,应当为 Null
    $this->assertNull($box->takeOne());
}

}

运行 phpunit 命令,你应当看到如下输出:

OK (3 tests, 6 assertions)

assertContains() 和 assertCount() 以及 assertEmpty()
终于,我们有三个作用于数组有关的断言,我们能够使用它们去检查 Box 类中的 startsWith($item) 方法。 assertContains() 断言传递进来的数组中包含指定值, assertCount() 断言数组的项数为指定数量,assertEmpty() 断言传递进来的数组为空。

让我们来执行以下测试:

<?php
use App\Box;

class BasicTest extends TestCase
{
public function testHasItemInBox()
{
$box = new Box([‘cat’, ‘toy’, ‘torch’]);

    $this->assertTrue($box->has('toy'));
    $this->assertFalse($box->has('ball'));
}

public function testTakeOneFromTheBox()
{
    $box = new Box(['torch']);

    $this->assertEquals('torch', $box->takeOne());

    // Null,现在这个 box 是空的。
    $this->assertNull($box->takeOne());
}

public function testStartsWithALetter()
{
    $box = new Box(['toy', 'torch', 'ball', 'cat', 'tissue']);

    $results = $box->startsWith('t');

    $this->assertCount(3, $results);
    $this->assertContains('toy', $results);
    $this->assertContains('torch', $results);
    $this->assertContains('tissue', $results);

    // 如果传递复数断言数组为空
    $this->assertEmpty($box->startsWith('s'));
}

}

保存并再一次运行你的测试:

OK (4 tests, 9 assertions)

恭喜你,你刚刚使用七个基础的 PHPUnit 断言完成了对 Box 类的全部测试。通过这些简单的断言你能够做许多事,对于其他断言,大多数要更复杂,不过它们仍遵循以上使用规则。

测试你的程序
在你的程序里,对每个组件进行单元测试在很多情况下都是有必要的,而且也应该成为你开发过程中必不可少的一部分,但这并不是你需要做的全部的测试。当你构建一个包含复杂视图、导航和表单的程序时,你同样想测试这些组件。这时,Laravel的测试助手可以使这些测试像单元测试简单组件一样容易。

我们之前查看在 ./tests/ 目录下的默认文件时跳过了 ./tests/ExampleTest.php 文件。 现在打开它,内容如下所示:

<?php
class ExampleTest extends TestCase
{
/**

  • 一个基本功能测试示例。
    *
  • @return void
    */
    public function testBasicExample()
    {
      $this->visit('/')
           ->see('Laravel 5');
    
    }
    }

我们可以看到这个测试示例非常简单。在不知道测试助手如何运作的情况下,我们可以猜测它的意思如下:

当我访问/ (根目录)
我应该看到 ‘Laravel 5’
如果你打开你的web浏览器,访问我们的程序(如果你没有启动你的web服务器,你可以运行 php artisan serve ),你应该可以在web根目录上看到屏幕上有“Laravel 5”的文本。 鉴于这个测试已经通过了PHPUnit,我们可以很确定地说我们对这个测试示例改造是正确的。

这个测试确保了访问/路径,网页可以返回“’Laravel 5”的文本。一个如此简单的检查也许不代表什么,但如果你的网站上要显示关键信息,它就可以在一个别处的改动导致这个页面无法正常显示正确的信息时,防止你部署一个被损坏的程序。

visit()、see() 以及 dontSee()
现在尝试编写自己的测试,更进一步理解它吧。

首先,编辑 ./app/Http/routes.php ,增加一个新的路由。为了教程目的,我们创建希腊字母定义的路由:

<?php
Route::get(‘/‘,function () {
return view(‘welcome’);
});

Route::get(‘/alpha’,function () {
return view(‘alpha’);
});

然后,创建视图文件 ./resources/views/alpha.blade.php,使用 Alpha 作为关键字,保存基本的HTML文件:

<!DOCTYPE html>

Alpha

This is the Alpha page.

打开浏览器,输入网址: http://localhost:8000/beta,页面会显示出 “This is the Alpha page.” 的内容。

现在我们有了测试用到的模版文件,下一步,我们通过运行命令 make:test 来创建一个新的测试文件:

php artisan make:test AlphaTest

然后变成刚创建好的测试文件,按照框架提供的例子,测试 “alpha” 页面上没有包含 “beta” 。 我们可以使用方法 dontSee() ,它是 see() 的对应的反向方法。

下面代码是上面实现的简单例子:

<?php
class AlphaTest extends TestCase
{
public function testDisplaysAlpha()
{
$this->visit(‘/alpha’)
->see(‘Alpha’)
->dontSee(‘Beta’);
}
}

保存并运行 PHPUnit (./vendor/bin/phpunit),测试代码应该会全部通过,你会看到像这样的测试状态内容显示:

OK (5 tests,12 assertions)

开发前先写测试
对于测试来说,测试驱动开发 (TDD) 是非常酷的方法,首先我们先写测试。写完测试并执行它们,你会发现测试没通过,接下来 我们编写满足测试的代码,再次执行测试,使测试通过。 接下来让我们开始。

首先,建立一个 BetaTest 类使用 make:test artisan 命令:

php artisan make:test BetaTest

接下来,更新测试用例以便检查 /beta 的路由 route 为「Beta」:

<?php
class BetaTest extends TestCase
{
public function testDisplaysBeta()
{
$this->visit(‘/beta’)
->see(‘Beta’)
->dontSee(‘Alpha’);
}
}

现在使用 ./vendor/bin/phpunit 命令来执行测试。结果是一个看起来简洁但不好的错误信息,如下:

./vendor/bin/phpunit
PHPUnit 4.8.19 by Sebastian Bergmann and contributors.

….F.

Time: 144 ms, Memory: 14.25Mb

There was 1 failure:

1) BetaTest::testDisplaysBeta
一个对 [http://localhost/beta] 的请求失败了。收到状态码 [404]。

FAILURES!
Tests: 6, Assertions: 13, Failures: 1.

我们现在需要创建这个不存在的路由。让我们开始。

首先,编辑 ./app/Http/routes.php 文件来创建新的 /beta 路由:

<?php
Route::get(‘/‘, function () {
return view(‘welcome’);
});

Route::get(‘/alpha’, function () {
return view(‘alpha’);
});

Route::get(‘/beta’, function () {
return view(‘beta’);
});

接下来,在 ./resources/views/beta.blade.php 下创建如下视图模版:

<!DOCTYPE html>

Beta

This is the Beta page.

现在再一次执行 PHPUnit,结果应该再一次回到绿色。

./vendor/bin/phpunit
PHPUnit 4.8.19 by Sebastian Bergmann and contributors.

……

Time: 142 ms, Memory: 14.00Mb

OK (6 tests, 15 assertions)

这样我们就通过在完成新的页面之前写测试的方式,对 测试驱动开发 进行了实践。

click() 和 seePageIs()
Laravel 也提供一个辅助函数 (click()) 允许测试点击页面中存在的连接 ,以及一个方法 (seePageIs()) 检查点击展示的结果页面。

让我们使用这两个辅助函数去执行在 Alpha 和 Beta 页面的链接。

首先,我们更新我们的测试。打开 AlphaTest 类,我们将添加一个新的测试方法,这将点击 「alpha」页面上的「Next」链接跳转到 「beta」页面。

新的测试代码如下:

<?php
class AlphaTest extends TestCase
{
public function testDisplaysAlpha()
{
$this->visit(‘/alpha’)
->see(‘Alpha’)
->dontSee(‘Beta’);
}

public function testClickNextForBeta()
{
    $this->visit('/alpha')
         ->click('Next')
         ->seePageIs('/beta');
}

}

注意到,在我们新建的 testClickNextForBeta() 方法中,我们并没有检查每一个页面的内容。 其他测试都成功的检查了两个页面的内容,所以这里我们只关心点击 「Next」链接将发送到 /beta。

你现在可以运行测试组件了,但就像预料的一样测试将不通过,因为我们还没有更新我们的 HTML。

接下来,我们将更新 BetaTest 来做类似的事情:

<?php
class BetaTest extends TestCase
{
public function testDisplaysBeta()
{
$this->visit(‘/beta’)
->see(‘Beta’)
->dontSee(‘Alpha’);
}

public function testClickNextForAlpha()
{
    $this->visit('/beta')
         ->click('Previous')
         ->seePageIs('/alpha');
}

}

接下来,我们更新我们的 HTML 模版。

./resources/views/alpha.blade.php:

<!DOCTYPE html>

Alpha

This is the Alpha page.

Next

./resources/views/beta.blade.php:

<!DOCTYPE html>

Beta

This is the Beta page.

Previous

保存文件,再一次执行 PHPUnit:

./vendor/bin/phpunit
PHPUnit 4.8.19 by Sebastian Bergmann and contributors.

F….F..

Time: 175 ms, Memory: 14.00Mb

There were 2 failures:

1) AlphaTest::testDisplaysAlpha
Failed asserting that ‘<!DOCTYPE html>

Alpha

This is the Alpha page.

Next


‘ does not match PCRE pattern “/Beta/i”.

2) BetaTest::testDisplaysBeta
Failed asserting that ‘<!DOCTYPE html>

Beta

This is the Beta page.

Previous


‘ does not match PCRE pattern “/Alpha/i”.

FAILURES!
Tests: 8, Assertions: 23, Failures: 2.

然而测试失败了。如果你仔细观察我们的新 HTML,你将注意到我们分别有术语 beta 和 alpha 在 /alpha 和 /beta 页面。这意味着我们需要稍微更改我们的测试让它们与误报不匹配。

在每一个 AlphaTest 和 BetaTest 类,更新 testDisplays* 方法去使用 dontSee(‘ page’)。通过这种方式,这将仅仅匹配字符串而不是那个术语。

两个测试文件如下所示:

./tests/AlphaTest.php:

<?php
class AlphaTest extends TestCase
{
public function testDisplaysAlpha()
{
$this->visit(‘/alpha’)
->see(‘Alpha’)
->dontSee(‘Beta page’);
}

public function testClickNextForBeta()
{
    $this->visit('/alpha')
         ->click('Next')
         ->seePageIs('/beta');
}

}

./tests/BetaTest.php:

<?php
class BetaTest extends TestCase
{
public function testDisplaysBeta()
{
$this->visit(‘/beta’)
->see(‘Beta’)
->dontSee(‘Alpha page’);
}

public function testClickNextForAlpha()
{
    $this->visit('/beta')
         ->click('Previous')
         ->seePageIs('/alpha');
}

}

再一次运行你的测试,所有的测试都应该通过了。我们现在已经测试我们所有的新文件,包括页面中的 Next/Previous 链接。

通过 Semaphore 对 PHPUnit 持续集成
通过 Semaphore设置 持续集成你可以自动执行你的测试。

这样每一次你进行 git push 提交代码的时候都会执行你的测试,并且 Semaphore 预装了所有最新的 PHP 版本。

如果你还没有一个 Semaphore 账户, 先去 注册一个免费的 Semaphore 账户 。接下来需要做的是将它 添加到你的项目,并按照提示逐步去做来执行你的测试:

composer install —prefer-source
phpunit

关于 PHP 持续集成 的更多信息,请参照 Semaphore 文档。

结语
你应该注意到本教程中的所有测试都有一个共同的主题:它们都非常简单。 这是学习如何使用基本的测试断言和辅助函数,并且尽可能的使用它们的好处之一。编写测试越简单,测试就越容易理解和维护。

掌握了本教程中介绍的 PHPUnit 断言之后,你还可以去 PHPUnit 文档 找到更多内容。 所有的断言都遵循基本的模式,但你会发现,在大多数测试中都会返回基本的断言。

对于 PHPUnit 断言来说,Laravel 的测试辅助函数是极好的补充,这让应用程序的测试变的非常容易。也就是说,重要的是要认识到,对于我们写测试,我们只检查关键信息,而不是整个页面。这使得测试变得简单,并允许页面内容随着应用程序的变化而变化。如果关键信息仍然存在,测试仍然通过,每个人都会满意。

在 Laravel 中基于 PHPUnit 进行代码测试:单元测试篇
由 学院君 创建于1年前, 最后更新于 5个月前 版本号 #1 5305 views 2 likes 0 collects
简介
介绍完 PHPUnit 的基本使用和 Laravel 框架自带的编排文件 phpunit.xml 文件,今天开始我们正式准备在 Laravel 项目中基于 PHPUnit 编写单元测试和功能测试,通过上篇教程介绍的编排文件我们知道,Laravel 的单元测试用例位于 tests/Unit 目录下,框架本身也为我们提供了一个示例测试文件 ExampleTest.php:

1
<?php
2

3
namespace Tests\Unit;
4

5
use Tests\TestCase;
6
use Illuminate\Foundation\Testing\RefreshDatabase;
7

8
class ExampleTest extends TestCase
9
{
10
/**
11

 * A basic test example.

12
*
13

 * @return void

14
*/
15
public function testBasicTest()
16
{
17
$this->assertTrue(true);
18
}
19
}
其中包含了一行最基本的断言测试,用于判断指定的参数是否为真,并且这个测试永远是通过的。Laravel 的单元测试其实是原原本本继承了 PHPUnit 的单元测试功能,这里的父类 Tests\TestCase 从根源上继承自 PHPUnit\Framework\TestCase,所以我们可以在测试用例中使用所有 PHPUnit 支持的断言方法和测试注解。

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

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

1
public function testVariables()
2
{
3
$bool = false;
4
$number = 100;
5
$arr = [‘Laravel’, ‘PHP’, ‘学院君’];
6
$obj = null;
7

8
// 断言变量值是否为假,与 assertTrue 相对
9
$this->assertFalse($bool);
10
// 断言给定变量值是否与期望值相等,与 assertNotEquals 相对
11
$this->assertEquals(100, $number);
12
// 断言数组中是否包含给定值,与 assertNotContains 相对
13
$this->assertContains(‘学院君’, $arr);
14
// 断言数组长度是否与期望值相等,与 assertNotCount 相对
15
$this->assertCount(3, $arr);
16
// 断言数组是否不会空,与 assertEmpty 相对
17
$this->assertNotEmpty($arr);
18
// 断言变量是否为 null,与 assertNotNull 相对
19
$this->assertNull($obj);
20
}
相应的断言用途在注释中已经说明了,我们可以对各种类型的变量从各种维度进行断言,甚至还可以对文件、目录、正则表达式进行断言,并且很多断言都可以从正反两个方法进行,相关的调用都很简单,你可以在需要的时候查看官方文档选择相应的断言方法:https://phpunit.readthedocs.io/zh_CN/latest/assertions.html。

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

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

1
public function testOutput()
2
{
3
$this->expectOutputString(‘学院君’);
4
echo ‘学院君’;
5
}
此外还可以对输出数据进行正则判断:

1
public function testOutputRegex()
2
{
3
$this->expectOutputRegex(‘/Laravel/i’);
4
echo ‘Laravel学院’;
5
}
上述测试结果都是通过的:

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

1
<?php
2
namespace App\Services;
3

4
class TestService
5
{
6
public function invalidArgument()
7
{
8
throw new \InvalidArgumentException(‘无效的参数’);
9
}
10
}
然后回到 Unit\ExampleTest,编写一个新的测试用例如下:

1
public function testException()
2
{
3
$this->expectException(\InvalidArgumentException::class);
4
$service = new TestService();
5
$service->invalidArgument();
6
}
运行测试用例,结果为通过:

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

1
$this->expectException(\Exception::class);
除此之外,还可以进一步对异常明细进行测试,比如通过 expectExceptionCode()、expectExceptionMessage() 和 expectExceptionMessageRegExp() 方法可以用于测试异常码、异常信息。

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

1
/**
2

  • @expectedException \InvalidArgumentException
    3
    */
    4
    public function testExceptionAnnotation()
    5
    {
    6
    $this->service->invalidArgument();
    7
    }
    由于两个测试用例中都用到了 TestService,所以我们将其在 setUp 方法进行初始化:

1
/**
2

  • @var TestService
    3
    */
    4
    protected $service;
    5

    6
    protected function setUp(): void
    7
    {
    8
    parent::setUp();
    9
    $this->service = new TestService();
    10
    }
    上面两个测试用例从功能上说是等价的:

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

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

1
protected $stack = [];
2

3
public function init()
4
{
5
$this->stack = [‘学院君’, ‘Laravel学院’, ‘单元测试’];
6
}
7

8
public function stackContains($value)
9
{
10
return in_array($value, $this->stack);
11
}
12

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

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

6
return $this->service;
7
}
8

9
/**
10

  • @depends testInitStack
    11
  • @param TestService $service
    12
    */
    13
    public function testStackContains(TestService $service)
    14
    {
    15
    $contains = $service->stackContains(‘学院君’);
    16
    $this->assertTrue($contains);
    17
    }
    在 testStackContains 用例中,我们将 testInitStack 用例返回的 $service 实例传递进来,并在此基础上进行测试。

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

1
public function initDataProvider()
2
{
3
return [
4
[‘学院君’],
5
[‘Laravel学院’]
6
];
7
}
8

9
/**
10

  • @depends testInitStack
    11
  • @dataProvider initDataProvider
    12
    */
    13
    public function testIsStackContains()
    14
    {
    15
    $arguments = func_get_args();
    16
    $service = $arguments[1];
    17
    $value = $arguments[0];
    18
    $this->assertTrue($service->stackContains($value));
    19
    }
    在这个测试用例中,我们通过 initDataProvider 方法作为数据提供器,数据供给器方法必须声明为 public,其返回值要么是一个数组,其每个元素也是数组;要么是一个实现了 Iterator 接口的对象,在对它进行迭代时每步产生一个数组。每个数组都是测试数据集的一部分,将以它的内容作为参数来调用测试方法。

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

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

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

0%