0

0

Laravel如何进行自动化测试?PHPUnit和PEST的示例分享

青灯夜游

青灯夜游

发布时间:2022-12-12 20:18:45

|

1770人浏览过

|

来源于learnku

转载

laravel如何进行自动化测试?下面本篇文章通过使用 phpunit 和 pest 的简单示例来了解如何在 laravel 中开始测试,希望对大家有所帮助。

Laravel如何进行自动化测试?PHPUnit和PEST的示例分享

在我们谈论任何编程语言的自动化测试或单元测试时,一般有两类人:

  • 那些不编写自动化测试并认为他们在浪费时间的人
  • 那些写测试然后无法想象没有他们的工作的人

所以,通过这篇文章,我将试图说服前者,看看它的好处,看看在 Laravel 中开始自动化测试是多么容易。【相关推荐:laravel视频教程

首先,让我们谈谈「为什么」,然后我将展示一些非常基本的「如何做测试」的示例。

立即学习PHP免费学习笔记(深入)”;


为什么需要自动化测试

自动化测试并不复杂:它们只是为你运行部分代码并报告任何错误。这是描述它们的最简单的方式。想象一下,你正在应用程序中启动一项新功能,然后一个机器人助理会为你手动测试新功能,同时测试新代码是否不会破坏旧功能的任何内容。

这样的好处是:自动重新测试所有功能。这似乎是额外的工作,但如果你不告诉那个「机器人」去做,那么你应该自己手动做,对吧?或者你在没有详细测试的情况下推出新功能,希望用户报告错误?我讽刺地称这种方法为「手指交叉驱动的开发」。

随着应用程序的每一项新功能,自动化测试的回报越来越高。

  • 功能 1:手动节省 X 分钟的测试时间
  • 功能 2:节省 2X 分钟 - 再次用于功能 2 和功能 1
  • 功能 3:节省 3X 分钟…
  • 等等。

你应该明白了。想象一下你的应用程序在一两年内,团队中的新开发人员甚至不知道「功能 1」如何运行或如何重现它以进行测试。所以,你未来的自己会非常感谢你编写自动化测试。

当然,如果你认为你的项目是一个非常短期的项目,并且你不太关心它的未来…… 不,我相信你的好意,所以让我告诉你开始测试是多么容易。


开始我们第一个自动化测试

要在 Laravel 中运行第一个自动化测试,你不需要编写任何代码。是的,你没看错。一切都已经在默认的 Laravel 安装中进行了配置和准备,包括第一个真正的基本示例。

你可以尝试安装一个 Laravel 项目并立即运行第一个测试:

laravel  new  project
cd  project
php  artisan  test

按照正常预期,终端将会输出如下结果:

 PASS  Tests\Unit\ExampleTest
✓ that true is true

 PASS  Tests\Feature\ExampleTest
✓ the application returns a successful response

Tests:  2 passed
Time:   0.10s

如果我们看一下默认的 Laravel /tests 文件夹,其中有两个文件。

tests/Feature/ExampleTest.php:

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

你无需了解任何语法即可读懂这段代码的含义:加载主页并检查 HTTP 状态代码是否「200 OK」。

你需要注意:在查看测试结果时,方法名称 test_the_application_returns_a_successful_response() 如何变为可读文本,只需将下划线符号替换为空格即可。

tests/Unit/ExampleTest.php:

class ExampleTest extends TestCase{
    public function test_that_true_is_true()
    {
        $this->assertTrue(true);
    }
}

这样的代码看上去让人感觉毫无意义,检查结果为 true 很必要吗?在后面片段中,我们将具体讨论单元测试。现在,你只需要了解每次测试中通常发生的情况。

  • tests/ 文件夹中的每个测试文件都是一个 PHP 类,扩展了 PHPUnit 的 TestCase
  • 在每个类中,你可以创建多个方法,通常一种方法用于一种情况进行测试
  • 每个方法内部都有三个动作:准备情况,然后动作,然后检查(断言)结果是否符合预期

从结构上讲,这就是你需要知道的全部内容,其他一切都取决于你要测试的确切内容。

要生成一个空的测试类,只需运行以下命令:

php artisan make:test HomepageTest

它会生成文件 tests/Feature/HomepageTest.php

class HomepageTest extends TestCase{
    // Replace this method with your own ones
    public function test_example()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

如果测试失败怎么办?

让我向你展示如果测试断言没有返回预期结果会发生什么。
让我们将示例测试编辑为:

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/non-existing-url');

        $response->assertStatus(200);
    }
}


class ExampleTest extends TestCase
{
    public function test_that_true_is_false()
    {
        $this->assertTrue(false);
    }
}

现在,如果我们再次运行 php artisan test

 FAIL  Tests\Unit\ExampleTest
⨯ that true is true

 FAIL  Tests\Feature\ExampleTest
⨯ the application returns a successful response

---

• Tests\Unit\ExampleTest > that true is true
Failed asserting that false is true.

at tests/Unit/ExampleTest.php:16
   12▕      * @return void
   13▕      */
   14▕     public function test_that_true_is_true()
   15▕     {
➜  16▕         $this->assertTrue(false);
   17▕     }
   18▕ }
   19▕

• Tests\Feature\ExampleTest > the application returns a successful response
Expected response status code [200] but received 404.
Failed asserting that 200 is identical to 404.

at tests/Feature/ExampleTest.php:19
   15▕     public function test_the_application_returns_a_successful_response()
   16▕     {
   17▕         $response = $this->get('/non-existing-url');
   18▕
➜  19▕         $response->assertStatus(200);
   20▕     }
   21▕ }
   22▕


Tests:  2 failed
Time:   0.11s

如你所见,有两个语句标记为 FAIL,下面有解释,箭头指向断言失败的确切测试行。所以这就是错误的显示方式。这非常的方便,不是吗?


简单示例:注册表单

让我们来看看一个现实生活中常见的例子。假设你有一个表单,你需要测试各种情况:检查是否填充无效数据是否失败,检查是否输入正确输入成功等。

你不一定知道,其实官方的 Laravel Breeze 入门套件附带了 内部功能测试?现在,让我们从那里看几个例子:

tests/Feature/RegistrationTest.php

use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class RegistrationTest extends TestCase
{
    use RefreshDatabase;

    public function test_registration_screen_can_be_rendered()
    {
        $response = $this->get('/register');

        $response->assertStatus(200);
    }

    public function test_new_users_can_register()
    {
        $response = $this->post('/register', [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password',
            'password_confirmation' => 'password',
        ]);

        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }
}

在这里,我们在一个类中有两个测试,因为它们都与注册表相关:一个是检查表单是否正确加载了,另一个是检查提交是否正常。

我们来熟悉另外两个检查结果的方法,另外两个断言: $this->assertAuthenticated()$response->assertRedirect()。 你可以查看 PHPUnit and Laravel Response 官方文档中所有可用的断言。请记住,一些一般的断言发生在 $this 对象上,而另一些检查则来自于路由调用的特定 $response 语句。

另一件重要的事情是 use RefreshDatabase; 语句,使用这个 trait,包含在这个类的上方。当你的测试操作可能会影响数据库时,需要使用它,例如在本例中,注册会在 users 数据库表中添加一个新条目。为此,你需要创建一个单独的测试数据库,该数据库将会在每次测试中使用 php artisan migrate:fresh 命令时被刷新。

你有两个选择:物理上创建一个单独的数据库,或者使用内存中的 SQLite 数据库。它都在 Laravel 默认提供的文件 phpunit.xml 中配置。具体来说, 你需要这部分:


    
    
    
    
    
    
    
    
    

看到被注释掉的 DB_CONNECTIONDB_DATABASE 了吗?如果你的服务器上有 SQLite,最简单的操作就是取消注释这些行,你的测试将在该内存数据库上运行。

在本次测试中,我们断言用户通过了身份验证,并被重定向到正确的首页,但我们也可以测试数据库中真实的数据。

除此代码之外:

$this->assertAuthenticated();
$response->assertRedirect(RouteServiceProvider::HOME);

我们也可以使用 Database Testing assertions 并执行以下操作:

$this->assertDatabaseCount('users', 1);

// 或者...
$this->assertDatabaseHas('users', [
    'email' => 'test@example.com',
]);

另外一个真实示例:登录表单

让我们看看另外一个来自 Laravel Breeze 的测试。

tests/Feature/AuthenticationTest.php:

Video Summarization
Video Summarization

一款可以自动将长视频制作成短片的桌面软件

下载
class AuthenticationTest extends TestCase
{
    use RefreshDatabase;

    public function test_login_screen_can_be_rendered()
    {
        $response = $this->get('/login');

        $response->assertStatus(200);
    }

    public function test_users_can_authenticate_using_the_login_screen()
    {
        $user = User::factory()->create();

        $response = $this->post('/login', [
            'email' => $user->email,
            'password' => 'password',
        ]);

        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }

    public function test_users_can_not_authenticate_with_invalid_password()
    {
        $user = User::factory()->create();

        $this->post('/login', [
            'email' => $user->email,
            'password' => 'wrong-password',
        ]);

        $this->assertGuest();
    }
}

这是关于登录表单的例子。他的逻辑和注册差不多吧?但不一样的是使用了三个方法而不是两个,所以这是一个测试好的和坏的场景的例子。所以,他们共同的逻辑是你应该测试的两种情况:什么时候顺利,什么时候失败。

此外,你在这个测试中看到的是 Database 工厂类 的使用:Laravel 创建了一个假用户(再次, 在你的测试数据库刷新) 上,然后尝试使用正确或不正确的凭据登录。

同样,Laravel 为 User 模型生成带有假数据的默认工厂,开箱即用。

database/factories/UserFactory.php:

class UserFactory extends Factory
{
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

看,有多少东西是 Laravel 本身提供的,所以我们很容易开始测试。

因此,如果我们在安装 Laravel Breeze 后运行 php artisan test, 我们应该会看到如下内容:

 PASS  Tests\Unit\ExampleTest
✓ that true is true

 PASS  Tests\Feature\Auth\AuthenticationTest
✓ login screen can be rendered
✓ users can authenticate using the login screen
✓ users can not authenticate with invalid password

 PASS  Tests\Feature\Auth\EmailVerificationTest
✓ email verification screen can be rendered
✓ email can be verified
✓ email is not verified with invalid hash

 PASS  Tests\Feature\Auth\PasswordConfirmationTest
✓ confirm password screen can be rendered
✓ password can be confirmed
✓ password is not confirmed with invalid password

 PASS  Tests\Feature\Auth\PasswordResetTest
✓ reset password link screen can be rendered
✓ reset password link can be requested
✓ reset password screen can be rendered
✓ password can be reset with valid token

 PASS  Tests\Feature\Auth\RegistrationTest
✓ registration screen can be rendered
✓ new users can register

 PASS  Tests\Feature\ExampleTest
✓ the application returns a successful response

Tests:  17 passed
Time:   0.61s

功能测试 VS 单元测试 VS 其他

你已经看到了 tests/Featuretests/Unit 子文件夹。两者之间有什么区别?答案有点“哲学”。

从测试的全局视角来看,在 Laravel/PHP 生态系统之外,有不同类型的自动化测试。你可以找到以下术语:

  • 单元测试
  • 功能测试
  • 集成测试
  • 功能测试
  • 端到端测试
  • 验收测试
  • 烟雾测试
  • 其他

这听起来很复杂,而且这些测试类型之间的实际差异有时是模糊的。这就是为什么 Laravel 简化了所有这些令人困惑的术语并将它们分为两类:单元测试/功能测试。

简而言之,功能测试尝试运行应用程序的实际功能:获取 URL、调用 API、模拟填写表单等确切行为。功能测试通常执行与任何项目用户在现实生活中手动执行的相同或相似的事情。

单元测试有两个含义。通常,你可能会发现任何自动化测试都称为「单元测试」,而整个过程可能称为「单元测试」。但是在功能与单元的上下文中,这个过程是关于单独测试代码的特定非公共单元。例如,你有一些 Laravel 类,它有一个计算某些东西的方法,比如带有参数的订单的总价格。因此,你的单元测试将断言该方法(代码单元)是否返回了具有不同参数的正确结果。

要生成单元测试,你需要添加一个标志:

php artisan make:test OrderPriceTest --unit

生成的代码与 Laravel 的默认单元测试相同:

class OrderPriceTest extends TestCase
{
    public function test_example()
    {
        $this->assertTrue(true);
    }
}

如你所见,没有 RefreshDatabase 行为的定义,这是单元测试最常见的定义之一:它不涉及数据库,它像一个「黑匣子」一样工作,与正在运行的应用程序隔离。

你可以尝试模仿我之前提到的示例,假设我们有一个服务类 OrderPrice

app/Services/OrderPriceService.php:

class OrderPriceService{
    public function calculatePrice($productId, $quantity, $tax = 0.0)
    {
        // 某种计算逻辑
    }
}

然后,单元测试可能看起来像这样:

class OrderPriceTest extends TestCase{
    public function test_single_product_no_taxes()
    {
        $product = Product::factory()->create(); // 生成假的产品数据
        $price = (new OrderPriceService())->calculatePrice($product->id, 1);
        $this->assertEquals(1, $price);
    }

    public function test_single_product_with_taxes()
    {
        $price = (new OrderPriceService())->calculatePrice($product->id, 1, 20);
        $this->assertEquals(1.2, $price);
    }

    // 更多的参数和案例
}

从我个人对 Laravel 项目的经验而言,绝大多数测试是功能测试,而不是单元测试。首先,你需要测试你的应用程序是否正常工作,以及真实用户使用它的方式。

接下来,如果你有可以定义为单元的特殊计算或逻辑,或带有一些参数,你可以专门为此创建单元测试。

有时候,编写测试需要更改代码本身,并将其重构为更「可测试的」:将单元分离为特殊的类或方法。


何时/如何运行测试?

php artisan test 命令的实际用途是什么,我们应该在什么时候运行它?

什么时候运行测试,在开发过程中并没有固定的时间节点或说法,具体取决于你公司的工作流程。通常情况下,在我们将最新的代码更改推送到代码仓库之前,你需要确保所有测试都是「绿色的」(意味着没有错误)。

因此,当你在本地编写代码,在你觉得自己已经完成了你的任务时,你需要运行测试,用来确保你没有破坏任何东西。请记住,你的代码可能不仅会在你自己编写的代码逻辑中导致错误,而且还会无意中破坏其他人很久以前编写的代码中的其他行为。

如果我们更进一步,可以自动化的完成很多事情。如使用各种 CI/CD 工具,你可以指定在有人将更改推送到特定 Git 分支时或在将代码合并到生产分支之前执行的测试。最简单的工作流程是使用 Github Actions,在这里,我提供了 一个单独的视频 演示它。


你应该测试什么?

关于所谓的「测试覆盖率」应该覆盖到多大的范围的争议,一直以来,有多种意见:你应该测试每个页面上的每个操作和每个可能的案例,还是只将你的工作限制在最重要的部分。

事实上,这就是我同意人们指责自动化测试花费更多时间而不是带来实际收益观点的地方。如果你为每个细节编写测试,这种情况就可能出现。也就是说,你的项目可能需要思考这个问题:「代码中潜在的错误会给你带来多大的成本或代价」。

换句话说,你需要通过“如果此代码失败会发生什么?”这个问题来确定你的测试工作的优先级。如果你的支付系统存在错误,这将直接影响业务。如果你的角色/权限功能被破坏,那这将是一个巨大的安全问题。

我喜欢 Matt Stauffer 在一次会议上的措辞:「你需要先测试这些东西,如果它们失败了,你就会被解雇」。当然,这有点夸张,但你明白了:首先测试重要的事情。然后是其他功能,如果你有时间的话。


PEST:PHPUnit 的新流行替代品

以上所有示例均基于默认的 Laravel 测试工具:PHPUnit。但多年来,生态系统中出现了其他工具,最新流行的工具之一是 laravel视频教程1。由 Laravel 官方员工 laravel视频教程2 创建,它的目标是简化语法,从而更快地编写测试代码。

在底层实现上,它基于 PHPUnit 运行;作为一个附属扩展,它只是试图最小化 PHPUnit 代码的一些默认重复部分。

让我们来看一个例子。还记得 Laravel 中默认的功能测试类吗?就如下面这段代码:

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

让我们使用 PEST 来实现同样的测试,实现后的代码如下:

test('the application returns a successful response')->get('/')->assertStatus(200);

是的,一行代码,就是这样。因此,PEST 的目标是解决以下问题:

  • 为一切创建类和方法;
  • 扩展测试用例;
  • 将所有操作放在一行代码上 – 在 PEST 中,你可以使用链式操作把不同动作串联起来。

要在 Laravel 中生成 PEST 测试,你需要指定一个附加标志:

php artisan make:test HomepageTest --pest

在撰写本文时,PEST 在 Laravel 开发人员中相当流行,但是除了众所周知的 PHPUnit 之外,是否使用这个额外的工具并学习它的语法是你个人的喜好。


因此,这就是你需要了解的有关自动化测试基础知识的全部内容。从这里开始,你可以选择创建哪些测试以及如何在你的项目中运行它们。

原文地址:https://laravel-news.com/how-to-start-testing译文地址:https://learnku.com/laravel/t/67381

更多编程相关知识,请访问:laravel视频教程3!!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
laravel组件介绍
laravel组件介绍

laravel 提供了丰富的组件,包括身份验证、模板引擎、缓存、命令行工具、数据库交互、对象关系映射器、事件处理、文件操作、电子邮件发送、队列管理和数据验证。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

319

2024.04.09

laravel中间件介绍
laravel中间件介绍

laravel 中间件分为五种类型:全局、路由、组、终止和自定。想了解更多laravel中间件的相关内容,可以阅读本专题下面的文章。

278

2024.04.09

laravel使用的设计模式有哪些
laravel使用的设计模式有哪些

laravel使用的设计模式有:1、单例模式;2、工厂方法模式;3、建造者模式;4、适配器模式;5、装饰器模式;6、策略模式;7、观察者模式。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

371

2024.04.09

thinkphp和laravel哪个简单
thinkphp和laravel哪个简单

对于初学者来说,laravel 的入门门槛较低,更易上手,原因包括:1. 更简单的安装和配置;2. 丰富的文档和社区支持;3. 简洁易懂的语法和 api;4. 平缓的学习曲线。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

374

2024.04.10

laravel入门教程
laravel入门教程

本专题整合了laravel入门教程,想了解更多详细内容,请阅读专题下面的文章。

85

2025.08.05

laravel实战教程
laravel实战教程

本专题整合了laravel实战教程,阅读专题下面的文章了解更多详细内容。

65

2025.08.05

laravel面试题
laravel面试题

本专题整合了laravel面试题相关内容,阅读专题下面的文章了解更多详细内容。

68

2025.08.05

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1898

2024.04.01

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

10

2026.01.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML教程
HTML教程

共500课时 | 5.1万人学习

PHP自制框架
PHP自制框架

共8课时 | 0.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号