首页 后端开发 php教程 Laravel 与 Sanctum 的单元测试

Laravel 与 Sanctum 的单元测试

Sep 07, 2024 am 10:30 AM

Pruebas Unitarias en Laravel con Sanctum

怎么样,我的孩子们,我希望你们度过了愉快的时光,度过了美好的一周,甚至是更好的一个月。我在 thedevgang.com 上写了这篇文章,并在这里分享它,以便与大家有更多的互动。我希望你喜欢它:3

已经是2024年的最后一个里程碑了,其他的事情,此时不值得谈论。好吧,在之前的博客文章中,我们将 Passport 身份验证库迁移到了 Sanctum,但是现在,我想深入研究一些端点的单元测试,从而能够在持续集成管道(例如 Github Actions)中执行它们

之前,我在 dev.to 中写过如何使用 Passport 进行单元测试,这篇文章可以在这里找到,我还解释了什么是单元测试以及它们在 Laravel 中实现的基本方面。在这篇文章中,我们将介绍以下内容:

    已实施 Sanctum 的单元测试
  • 测试一些端点
实施 Sanctum 的单元测试

在这篇文章中,我为我已经开发了几个月的替代项目整理了一些端点。该项目在框架等方面具有以下特点:

    Laravel 11 与 Sanctum 4
  • PHP 单元 10
  • Laravel Sail 作为开发环境
在这种情况下,我们将测试为此应用程序的身份验证过程设置的三个端点,首先我们将使用以下方法执行适当的操作:


public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email',
            'password' => 'required',
            'device_id' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['success' => false, 'error' => $validator->errors()], $this->badRequestStatus);
        }

        $result = $this->getToken(request('email'), request('password'), request('device_id'));

        if ($result['success'] == true) {
            return response()->json($result, $this->successStatus);
        } else {
            return response()->json(['success' => false, 'error' => 'Unauthorized'], $this->unauthorizedStatus);
        }
    }
这个方法完全管理我们应用程序的登录过程,但是注册不包含在这个代码片段中,这将是下一个要测试的方法。在这种情况下,我们已经确认了它并且似乎可以正常工作,但为了确保这一点,我们将设置各自的测试。

首先在终端输入此命令:

php artisan make:test UserTest --unit

这将在tests/Unit文件夹中创建一个UserTest文件,该文件将完全“空白”,如下所示:


<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $this->assertTrue(true);
    }
}
删除 test_basic_test() 方法,我们不再需要它了。在这种情况下,我说它是空白的,因为它只是我们单元测试的模拟,在这种情况下,它将是我们用于上述方法的测试。现在,在开始安排测试之前,我们需要确保我们将执行和测试的用例,因此我们有以下用例要测试:

    登录正确。
  1. 输入所有数据的登录无效。
  2. 正确注册。
  3. 正确的个人资料注册。
  4. 由于未输入数据而导致个人资料注册错误。
  5. 找不到个人资料。
  6. 注册正确的个人资料及其反馈。
列出用例后,我们考虑到上述方法在本例中涵盖的是用例 1 和 2,因此我们将继续处理它们。

测试准备

现在,在开始编写测试代码之前,我们需要配置它们以便它们能够正确执行。为此,我们将在 UserTest 文件中创建 setUp 方法,该方法在执行单元测试之前执行指令。 。在这里,我们可以告诉系统必须执行迁移,并且在需要数据时能够开始迁移,以及为变量赋值。我们将创建的 setUp 方法的结构如下:


public function setUp(): void
    {
        parent::setUp();
        $this->faker = \Faker\Factory::create();

        $this->name = $this->faker->name();
        $this->password = 'password';
        $this->email = 'valid@test.com';
        $this->deviceId = $this->faker->uuid();

        Artisan::call('migrate:fresh', ['-vvv' => true]);
    }
设置将执行以下操作:

    创建Faker的实例,这是一个模拟各种类型变量的数据输入的库。
  • 我们创建了一个虚构的名字
  • 我们将密码和电子邮件分配为默认值。
  • 我们还为伪造者分配了一个虚构的设备 ID。
  • 将运行数据库迁移
在此方法之上,声明我们将用于所有测试的全局变量:


public $faker;
public $name;
public $email;
public $password;
public $deviceId;
单元测试的开发

对于测试 1,我们需要通过调用我们将在应用程序中调用的端点来确保登录正确。我们将创建 test_login_success 方法,它看起来像这样:


public function test_login_success()
    {
        Artisan::call('db:seed', ['-vvv' => true]);

        $body = [
            'email' => $this->email,
            'password' => $this->password,
            'device_id' => $this->deviceId
        ];

        $this->json('POST', '/api/login', $body, ['Accept' => 'application/json'])
            ->assertStatus(200)->assertJson([
                "success" => true
            ]);
    }

Este método, primeramente alimentará la base de datos con los catálogos pertinentes para poder confirmar que los mismos existen sin problemas. Después asignará el body y enviará los datos por medio de un request POST, al enviarlo, revisará que el status que devuelva su llamada es 200 y que los datos sean conforme al arreglo solicitado para confirmar, en este caso [ “success” => true ]. Si todo sale bien y se cumplen las condiciones, se considera prueba satisfactoria, en caso contrario, se considerará fallida y es donde se tendrá que revisar nuevamente el código.

Ahora bien, haremos el caso de uso 2. Para ello crea un método llamado test_login_error_with_data_ok e ingresa el siguiente código:

public function test_login_error_with_data_ok()
    {
        Artisan::call('db:seed', ['-vvv' => true]);

        $body =  [
            'email' => 'invalid@test.com',
            'password' => 'password',
            'device_id' => $this->deviceId
        ];

        $this->json('POST', '/api/login', $body)
            ->assertStatus(401)->assertJson([
                "success" => false
            ]);
    }

A diferencia del anterior, en este caso, se le entregan datos erróneos y se solicita que confirme que el endpoint devuelva un error 401, así como un body [“success” => false ], esto con el fin de que se confirme que el sistema deniega el acceso a alguien que no tenga credenciales correctas.

Con esto, cubrimos el método presentado anteriormente y ya quedaría cubierto el método. Para poder probarlo, podemos ejecutar el siguiente comando bajo Sail:

docker compose exec laravel.test php artisan test

Te mostrará los siguientes resultados:

PASS  Tests\Unit\UserTest
  ✓ login error with data ok 0.08s  
  ✓ login success 0.16s

Si te sale todo bien como te lo he mostrado, tus unit tests han salido satisfactoriamente, pero estamos lejos de terminar. Ahora necesitamos probar el siguiente método:

public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required|email|unique:users',
            'password' => 'required',
            'c_password' => 'required|same:password',
            'device_id' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['success' => false, 'error' => $validator->errors()], $this->badRequestStatus);
        }

        $password = $request->password;
        $input = $request->all();
        $input['password'] = bcrypt($password);
        $user = User::create($input);

        if (null !== $user) {
            $result = $this->getToken($user->email, $password, $request->device_id);

            if ($result['success'] == true) {
                return response()->json($result, $this->successStatus);
            } else {
                return response()->json(['success' => false, 'error' => 'Unauthorized'], $this->unauthorizedStatus);
            }
        }
    }

En este caso, realizaremos el caso de uso 3, el cual solicita confirmar que el registro sea correcto, para ello, crea el método test_register_success e ingresa el siguiente código:

public function test_register_success()
    {
        $body = [
            'name' => $this->name,
            'email' => $this->email,
            'password' => $this->password,
            'c_password' => $this->password,
            'device_id' => $this->deviceId
        ];

        $this->json('POST', '/api/register', $body)
            ->assertStatus(200)->assertJson([
                "success" => true
            ]);
    }

Al igual que con el login, solicitamos que nos confirme el sistema que se nos está entregando un código 200 así como el arreglo [“success” => true], si logramos eso, ya hemos terminado, pero si te das cuenta, nos hace falta la prueba en caso de que se equivoque el usuario. Ese método te lo dejo de tarea para que puedas corroborar tus conocimientos.

Ahora bien probaremos los siguientes métodos:

public function profile()
    {
        $user = Auth::user();
        $profile = Profile::find($user->id);

        if (null !== $profile) {
            return response()->json(["success" => true, "data" => $user], $this->successStatus);
        } else {
            return response()->json(['success' => false, 'message' => 'Usuario no encontrado.'], $this->notFoundStatus);
        }
    }
public function createProfile(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'first_name' => 'required',
                'last_name' => 'required',
                'birth_date' => 'required|date',
                'bloodtype' => 'required|numeric',
                'phone' => 'required',
                'gender' => 'required|numeric',
                'country' => 'required|numeric',
                'state' => 'required|numeric',
            ]);

            if ($validator->fails()) {
                return response()->json(['success' => false, 'error' => $validator->errors()], $this->badRequestStatus);
            }

            $user = Auth::user();
            $profile = Profile::where(['user_id' => $user->id])->first();

            $data = [
                'user_id' => $user->id,
            ];

            $dataInsert = array_merge($data, $request->all());

            if (null !== $profile) {
                $profile = $profile->update($dataInsert);
            } else {
                $profile = Profile::create($dataInsert);
            }


            return response()->json(["success" => true, "message" => 'Perfil actualizado correctamente.'], $this->successStatus);
        } catch (QueryException $e) {
            return response()->json(["success" => false, "message" => 'Error al actualizar el perfil.'], $this->internalServerErrorStatus);
        }
    }

Este par de métodos son los referentes a la gestión del perfil del usuario y su retroalimentación, por lo que los casos de uso que debemos probar son del 4 al 7. Para el caso 4, debemos crear un nuevo método llamado test_register_profile_success y agregamos el siguiente código:

public function test_register_profile_success()
    {
        $body = [
            'first_name' => $this->faker->firstName,
            'last_name' => $this->faker->lastName,
            'birth_date' => '1987-10-10',
            'bloodtype' => 1,
            'phone' => $this->faker->phoneNumber,
            'gender' => 1,
            'country' => 1,
            'state' => 1,
        ];

        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->post('/api/user/profile', $body);

        $response->assertStatus(200);
    }

En esta ocasión, necesitamos declarar un arreglo que simule el contenido del cuerpo del request para que pueda ser enviado correctamente por el endpoint y una vez enviado, el confirmar que el request tiene una respuesta satisfactoria (200).

Para el caso del perfil erróneo por no ingresar datos, necesitamos agregar un nuevo método que denominaremos test_register_profile_validation_failed, el cual implementaremos de la siguiente forma:

public function test_register_profile_validation_failed()
    {
        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->post('/api/user/profile', []);

        $response->assertStatus(400);
    }

En este caso, es prácticamente el mismo contenido de la prueba anterior, con la diferencia que ahora le enviamos un arreglo en blanco, para poder asegurarnos que si no se están enviando los datos correctamente, no permita la creación del perfil del usuario por medio de un Bad Request error (400).

El siguiente método probará que en caso de no encontrar el perfil de algún usuario, así lo indique con un código 404, por lo que creamos otro método denominado test_obtain_profile_not_found e ingresando el siguiente código.

public function test_obtain_profile_not_found()
    {
        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->get('/api/user/profile');

        $response->assertStatus(404);
    }

En el modelo de negocio, nosotros al registrarnos, creamos el usuario, mas no el perfil que tiene que ser ingresado posteriormente, por lo que al momento de ejecutar la prueba unitaria, al ejecutar el request para obtener el perfil, nos enviará un código 404, comportamiento que estamos buscando para esta prueba unitaria.

Finalmente para el último caso de uso, crearemos el método test_register_profile_and_obtain para confirmar que un mismo test pueda obtener dos comportamientos en un mismo flujo. Para este caso implementaremos el siguiente código:

public function test_register_profile_and_obtain()
    {
        $body = [
            'first_name' => $this->faker->firstName,
            'last_name' => $this->faker->lastName,
            'birth_date' => '1987-10-10',
            'bloodtype' => 1,
            'phone' => $this->faker->phoneNumber,
            'gender' => 1,
            'country' => 1,
            'state' => 1,
        ];

        $user = User::factory()->create();
        $token = $user->createToken('TestToken')->plainTextToken;

        $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->post('/api/user/profile', $body);

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token,
        ])->get('/api/user/profile');

        $response->assertStatus(200);
    }

En este test, implementamos dos casos de uso realizados previamente, el primero es la creación del perfil y posteriormente, retroalimentamos el perfil, indicando a PHPUnit que deseamos confirmar que el response del endpoint que retroalimenta el perfil sea satisfactoria (código 200). Igualmente podríamos realizar el assert de la inserción de datos cambiando algunas líneas de código, pero por el momento es más que suficiente.

Ya terminando las pruebas unitarias, procedemos a ejecutar el comando docker compose exec laravel.test php artisan test y confirmamos el estatus de nuestras pruebas unitarias. Si nos salen de esta forma:

PASS  Tests\Unit\UserTest
  ✓ login error with data ok.                 0.10s  
  ✓ login success.                            0.15s  
  ✓ register success.                         0.20s  
  ✓ register profile success.                 0.10s  
  ✓ register profile validation failed.       0.09s  
  ✓ obtain profile not found.                 0.10s  
  ✓ register profile and obtain.              0.10s  

Las pruebas unitarias salieron satisfactorias. En caso contrario, checa lo siguiente:

  • 出现问题的方法,检查是否是代码情况。
  • 检查 PHPUnit 配置是否合适,我们将在下一篇文章中深入研究。

同样,我将解释如何配置 Github Actions 以对其运行单元测试,甚至能够获取代码覆盖率报告和可能的持续部署。我希望这篇文章虽然很长,但能为您提供有关单元测试以及持续集成和部署过程的更多背景信息。

编码愉快!

以上是Laravel 与 Sanctum 的单元测试的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

PHP教程
1543
276
PHP调用AI智能语音助手 PHP语音交互系统搭建 PHP调用AI智能语音助手 PHP语音交互系统搭建 Jul 25, 2025 pm 08:45 PM

用户语音输入通过前端JavaScript的MediaRecorderAPI捕获并发送至PHP后端;2.PHP将音频保存为临时文件后调用STTAPI(如Google或百度语音识别)转换为文本;3.PHP将文本发送至AI服务(如OpenAIGPT)获取智能回复;4.PHP再调用TTSAPI(如百度或Google语音合成)将回复转为语音文件;5.PHP将语音文件流式返回前端播放,完成交互。整个流程由PHP主导数据流转与错误处理,确保各环节无缝衔接。

如何用PHP搭建社交分享功能 PHP分享接口集成实战 如何用PHP搭建社交分享功能 PHP分享接口集成实战 Jul 25, 2025 pm 08:51 PM

在PHP中搭建社交分享功能的核心方法是通过动态生成符合各平台要求的分享链接。1.首先获取当前页面或指定的URL及文章信息;2.使用urlencode对参数进行编码;3.根据各平台协议拼接生成分享链接;4.在前端展示链接供用户点击分享;5.动态生成页面OG标签优化分享内容展示;6.务必对用户输入进行转义以防止XSS攻击。该方法无需复杂认证,维护成本低,适用于大多数内容分享需求。

如何用PHP结合AI实现文本纠错 PHP语法检测与优化 如何用PHP结合AI实现文本纠错 PHP语法检测与优化 Jul 25, 2025 pm 08:57 PM

要实现PHP结合AI进行文本纠错与语法优化,需按以下步骤操作:1.选择适合的AI模型或API,如百度、腾讯API或开源NLP库;2.通过PHP的curl或Guzzle调用API并处理返回结果;3.在应用中展示纠错信息并允许用户选择是否采纳;4.使用php-l和PHP_CodeSniffer进行语法检测与代码优化;5.持续收集反馈并更新模型或规则以提升效果。选择AIAPI时应重点评估准确率、响应速度、价格及对PHP的支持。代码优化应遵循PSR规范、合理使用缓存、避免循环查询、定期审查代码,并借助X

PHP打造博客评论系统变现 PHP评论审核与防刷策略 PHP打造博客评论系统变现 PHP评论审核与防刷策略 Jul 25, 2025 pm 08:27 PM

1.评论系统商业价值最大化需结合原生广告精准投放、用户付费增值服务(如上传图片、评论置顶)、基于评论质量的影响力激励机制及合规匿名数据洞察变现;2.审核策略应采用前置审核 动态关键词过滤 用户举报机制组合,辅以评论质量评分实现内容分级曝光;3.防刷需构建多层防御:reCAPTCHAv3无感验证、Honeypot蜜罐字段识别机器人、IP与时间戳频率限制阻止灌水、内容模式识别标记可疑评论,持续迭代应对攻击。

PHP实现商品库存管理变现 PHP库存同步与报警机制 PHP实现商品库存管理变现 PHP库存同步与报警机制 Jul 25, 2025 pm 08:30 PM

PHP通过数据库事务与FORUPDATE行锁确保库存扣减原子性,防止高并发超卖;2.多平台库存一致性需依赖中心化管理与事件驱动同步,结合API/Webhook通知及消息队列保障数据可靠传递;3.报警机制应分场景设置低库存、零/负库存、滞销、补货周期和异常波动策略,并按紧急程度选择钉钉、短信或邮件通知责任人,且报警信息需完整明确,以实现业务适配与快速响应。

如何用PHP结合AI做图像生成 PHP自动生成艺术作品 如何用PHP结合AI做图像生成 PHP自动生成艺术作品 Jul 25, 2025 pm 07:21 PM

PHP不直接进行AI图像处理,而是通过API集成,因为它擅长Web开发而非计算密集型任务,API集成能实现专业分工、降低成本、提升效率;2.整合关键技术包括使用Guzzle或cURL发送HTTP请求、JSON数据编解码、API密钥安全认证、异步队列处理耗时任务、健壮错误处理与重试机制、图像存储与展示;3.常见挑战有API成本失控、生成结果不可控、用户体验差、安全风险和数据管理难,应对策略分别为设置用户配额与缓存、提供prompt指导与多图选择、异步通知与进度提示、密钥环境变量存储与内容审核、云存

如何用PHP开发问答社区平台 PHP互动社区变现模式详解 如何用PHP开发问答社区平台 PHP互动社区变现模式详解 Jul 23, 2025 pm 07:21 PM

1.PHP开发问答社区首选Laravel MySQL Vue/React组合,因生态成熟、开发效率高;2.高性能需依赖缓存(Redis)、数据库优化、CDN和异步队列;3.安全性必须做好输入过滤、CSRF防护、HTTPS、密码加密及权限控制;4.变现可选广告、会员订阅、打赏、佣金、知识付费等模式,核心是匹配社区调性和用户需求。

超越灯堆:PHP在现代企业体系结构中的作用 超越灯堆:PHP在现代企业体系结构中的作用 Jul 27, 2025 am 04:31 AM

PHPisstillrelevantinmodernenterpriseenvironments.1.ModernPHP(7.xand8.x)offersperformancegains,stricttyping,JITcompilation,andmodernsyntax,makingitsuitableforlarge-scaleapplications.2.PHPintegrateseffectivelyinhybridarchitectures,servingasanAPIgateway

See all articles