Laravel자동 테스트를 수행하는 방법은 무엇입니까? 다음 기사에서는 PHPUnit과 PEST를 사용하여 Laravel에서 테스트를 시작하는 방법을 배우는 간단한 예제를 사용하여 모든 사람에게 도움이 되기를 바랍니다.
프로그래밍 언어에 대한 자동화된 테스트 또는 단위 테스트에 관해 이야기할 때 일반적으로 사람들은 두 가지 범주로 분류됩니다.
그래서 이 글을 통해 저는 전자에게 그 이점을 확인하고 Laravel에서 자동 테스트를 시작하는 것이 얼마나 쉬운지 확인하도록 설득하려고 노력할 것입니다. [관련 추천: 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
를 보면 두 개의 파일이 들어 있는 폴더입니다.
:
class ExampleTest extends TestCase { public function test_the_application_returns_a_successful_response() { $response = $this->get('/'); $response->assertStatus(200); } }
이 코드의 의미를 이해하기 위해 구문을 알 필요는 없습니다. 홈 페이지를 로드하고 HTTP 상태 코드가 "200 OK"인지 확인하세요. /tests
文件夹,其中有两个文件。
tests/Feature/ExampleTest.php:
class ExampleTest extends TestCase{ public function test_that_true_is_true() { $this->assertTrue(true); } }
你无需了解任何语法即可读懂这段代码的含义:加载主页并检查 HTTP 状态代码是否「200 OK」。
你需要注意:在查看测试结果时,方法名称 test_the_application_returns_a_successful_response()
如何变为可读文本,只需将下划线符号替换为空格即可。
tests/Unit/ExampleTest.php:
php artisan make:test HomepageTest
这样的代码看上去让人感觉毫无意义,检查结果为 true 很必要吗?在后面片段中,我们将具体讨论单元测试。现在,你只需要了解每次测试中通常发生的情况。
tests/
文件夹中的每个测试文件都是一个 PHP 类,扩展了 PHPUnit 的 TestCase从结构上讲,这就是你需要知道的全部内容,其他一切都取决于你要测试的确切内容。
要生成一个空的测试类,只需运行以下命令:
class HomepageTest extends TestCase{ // Replace this method with your own ones public function test_example() { $response = $this->get('/'); $response->assertStatus(200); } }
它会生成文件 tests/Feature/HomepageTest.php
:
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); } }
让我向你展示如果测试断言没有返回预期结果会发生什么。
让我们将示例测试编辑为:
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
现在,如果我们再次运行 php artisan test
test_the_application_returns_a_successful_response()
가 어떻게 읽을 수 있는 텍스트로 변하는지, 밑줄 기호를 공백으로 바꾸면 됩니다. 🎜tests/Unit/ExampleTest.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); } }🎜이러한 코드는 의미가 없어 보이는데 결과가 맞는지 확인해야 하나요? 이후 부분에서는 단위 테스트에 대해 자세히 논의하겠습니다. 지금은 각 테스트에서 일반적으로 어떤 일이 발생하는지 이해하면 됩니다. 🎜🎜🎜
tests/
폴더의 각 테스트 파일은 를 확장하는 PHP 클래스입니다. PHPUnit의 TestCase🎜🎜🎜각 클래스에서 여러 메소드를 생성할 수 있습니다. 일반적으로 하나의 메소드는 하나의 상황에 대해 테스트하는 데 사용됩니다.🎜🎜각 메소드에는 세 가지 작업이 있습니다. 상황을 준비한 다음 작업을 수행하고 확인(어설션)합니다. 결과는 예상한 대로입니다. 🎜🎜🎜 구조적으로 이것이 알아야 할 전부이며, 다른 모든 것은 정확히 테스트하려는 대상에 따라 다릅니다. 🎜🎜빈 테스트 클래스를 생성하려면 다음 명령을 실행하세요. 🎜<php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <!-- <env name="DB_CONNECTION" value="sqlite"/> --> <!-- <env name="DB_DATABASE" value=":memory:"/> --> <env name="MAIL_MAILER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="TELESCOPE_ENABLED" value="false"/> </php>🎜
tests/Feature/HomepageTest.php
파일이 생성됩니다. 🎜$this->assertAuthenticated(); $response->assertRedirect(RouteServiceProvider::HOME);🎜🎜🎜테스트가 실패하면 어떻게 되나요? 🎜🎜🎜테스트 어설션이 예상한 결과를 반환하지 않으면 어떤 일이 발생하는지 보여드리겠습니다.
$this->assertDatabaseCount('users', 1); // 或者... $this->assertDatabaseHas('users', [ 'email' => 'test@example.com', ]);🎜이제
php artisan test
를 다시 실행하면 다음과 같습니다. 🎜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(); } }🎜보시다시피 아래에 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
中配置。具体来说, 你需要这部分:
<php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <!-- <env name="DB_CONNECTION" value="sqlite"/> --> <!-- <env name="DB_DATABASE" value=":memory:"/> --> <env name="MAIL_MAILER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="TELESCOPE_ENABLED" value="false"/> </php>
看到被注释掉的 DB_CONNECTION
和 DB_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:
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
你已经看到了 tests/Feature
和 tests/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 在一次会议上的措辞:「你需要先测试这些东西,如果它们失败了,你就会被解雇」。当然,这有点夸张,但你明白了:首先测试重要的事情。然后是其他功能,如果你有时间的话。
以上所有示例均基于默认的 Laravel 测试工具:PHPUnit。但多年来,生态系统中出现了其他工具,最新流行的工具之一是 PEST。由 Laravel 官方员工 Nuno Maduro 创建,它的目标是简化语法,从而更快地编写测试代码。
在底层实现上,它基于 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 的目标是解决以下问题:
要在 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로 테스트를 자동화하는 방법은 무엇입니까? PHPUnit 및 PEST 공유 예시의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!