Laravel 第十章 API测试和文档

一、API自动化测试介绍

API 自动化测试

我们已经完成了 Larabbs 所有的接口开发,接下来我们需要将接口交付给其他的工程师对接。将接口部署到生产环境时,如何确保交付的接口正确稳定呢?后续我们还会为项目新增功能,到那时,我们如何保证代码升级过后,接口依然正常?

在开发过程中,我们使用 PostMan 手动一个个接口测试,可当我们有几十个甚至上百个接口时,要同时测试这些接口,手动测试将无法适用。解决方案是自动化测试,自动化测试是保证项目质量的重要环节,这一节我们来了解一下测试的相关概念。

单元测试

单元测试是指对软件中的最小可测试单元进行检查和验证,对于 PHP 来说通常情况下是对某个类中的 某个方法,或者单独的某个方法进行测试。单元测试的目的是首先保证一个系统的 基本组成单元 能正常工作,所有基础零件工作正常了,组装出来的软件才不会出问题。

单元测试是 代码级别 的测试,开发和维护成本都很高,不建议小团队使用。如果是多人协作,我的任务是单独的封装一些通用功能,譬如写个 Service;或者编写一个扩展包,提供一些底层的代码接口,譬如为某个第三方应用封装 SDK,那么单元测试非常有必要。

单元测试涉及的知识点很多,本教程中,我们不对单元测试进行单独讲解。

API 集成测试

Laravel 框架自带的集成 API 测试,我们初始化完整的应用程序上下文,准备好数据库中的测试数据后,就可以方便的模拟各种请求方式,调用接口获取响应结果,最终 断言 返回的结果是否等于预期结果。

API 集成测试 需要准备测试数据,需要设置用户,分配用户权限,为测试接口准备好相关联的数据。虽然有一定得维护成本,但是相比单元测试,维护成本要少很多,在一定程度上更能够保证项目的健壮性。

PostMan 测试

使用 PostMan 等工具进行手动测试,这种是最推崇的测试方案,非常适合小团队使用,因为可以比较真实地模拟用户请求,并且团队协作中,后端工程师可以将 PostMan 的调试接口分享给客户端工程师,接口是否有问题客户端工程师可以自行测试。

二、Laravel API 集成测试

新建文件夹  到要新建文件夹的父目录中,$mkdir 文件夹名称

这一节我们通过几个例子来学习 API 集成测试。

如果你正在使用或者升级到了 Laravel 5.7,那么 phpunit 的版本应该是 7.*

PHPUnit

PHPUnit 是一个轻量级的 PHP 测试框架,Laravel 默认就支持用 PHPUnit 来做测试,并为你的应用程序配置好了 phpunit.xml 文件,只需在命令行上运行 phpunit 就可以进行测试。

现在的 Larabbs 项目中有个小错误,会导致运行 phpunit 的时候报错 PHP Fatal error: Cannot redeclare route_class()。只是因为我们的自定义方法的引入有些问题,稍微修改一下:

bootstrap/app.php

.
.
.
require_once __DIR__ . '/helpers.php';
.
.
.

之前的代码中使用的是 require 引入自定义方法,可能会重复引入,所以这里改为 require_once

尝试在 larabbs 根目录执行 phpunit

$ phpunit


创建测试文件

首先需要创建一个测试文件:

$ php artisan make:test TopicApiTest

该命令会在 tests/Feature 目录中创建 TopicApiTest.php 文件,我们会发现 tests 目录中有 Feature 和 Unit 两个目录,如何区分这两个目录呢?

  • Unit —— 单元测试是从程序员的角度编写的。它们用于确保类的特定方法执行一组特定任务。
  • Feature —— 功能测试是从用户的角度编写的。它们确保系统按照用户期望的那样运行,包括几个对象的相互作用,甚至是一个完整的 HTTP 请求。

我们测试 API 属于功能测试,应该创建在 Feature 目录中。


测试发布话题

tests/Feature/TopicApiTest.php

<?php

namespace Tests\Feature;

use App\Models\User;
use App\Models\Topic;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class TopicApiTest extends TestCase
{
    protected $user;

    public function setUp()
    {
        parent::setUp();

        $this->user = factory(User::class)->create();
    }

    public function testStoreTopic()
    {
        $data = ['category_id' => 1, 'body' => 'test body', 'title' => 'test title'];

        $token = \Auth::guard('api')->fromUser($this->user);
        $response = $this->withHeaders(['Authorization' => 'Bearer '.$token])
            ->json('POST', '/api/topics', $data);

        $assertData = [
            'category_id' => 1,
            'user_id' => $this->user->id,
            'title' => 'test title',
            'body' => clean('test body', 'user_topic_body'),
        ];

        $response->assertStatus(201)
            ->assertJsonFragment($assertData);
    }
}

setUp 方法会在测试开始之前执行,我们先创建一个用户,测试会以该用户的身份进行测试。

testStoreTopic 就是一个测试用户,测试发布话题。使用 $this->json 可以方便的模拟各种 HTTP 请求:

  • 第一个参数 —— 请求的方法,发布话题使用的是 POST 方法;
  • 第二个参数 —— 请求地址,请求 /api/topics
  • 第三个参数 —— 请求参数,传入 category_idbodytitle,这三个必填参数;
  • 第四个参数 —— 请求 Header,可以直接设置 header,也可以利用 withHeaders 方法达到同样的目的;

我们发现为用户生成 Token 以及设置 Authorization 部分的代码,不仅 修改话题删除话题 会使用,以后编写的其他功能的测试用例一样会使用,所以我们进行一下封装。

增加一个 Trait:

$ touch tests/Traits/ActingJWTUser.php 

tests/Traits/ActingJWTUser.php

<?php

namespace Tests\Traits;

use App\Models\User;

trait ActingJWTUser
{
    public function JWTActingAs(User $user)
    {
        $token = \Auth::guard('api')->fromUser($user);
        $this->withHeaders(['Authorization' => 'Bearer '.$token]);

        return $this;
    }
}

修改测试用例,使用该 Trait。

tests/Feature/TopicApiTest.php

<?php

namespace Tests\Feature;
.
.
.
use Tests\Traits\ActingJWTUser;
.
.
.
class TopicApiTest extends TestCase
{
    use ActingJWTUser;
.
.
.
    public function testStoreTopic()
    {
        $data = ['category_id' => 1, 'body' => 'test body', 'title' => 'test title'];

        $response = $this->JWTActingAs($this->user)
            ->json('POST', '/api/topics', $data);

        $assertData = [
            'category_id' => 1,
            'user_id' => $this->user->id,
            'title' => 'test title',
            'body' => clean('test body', 'user_topic_body'),
        ];

        $response->assertStatus(201)
            ->assertJsonFragment($assertData);
    }
}

这样我们就能方便的使用 JWTActingAs 方法,登录一个用户。最后得到的响应 $response,通过 assertStatus 断言响应结果为 201,通过 assertJsonFragment 断言响应结果包含 assertData 数据。

执行测试:

$ phpunit

卡在此处一直报错:Error: Call to undefined method Illuminate\Auth\TokenGuard::fromUser()

等待解决


测试修改话题

tests/Feature/TopicApiTest.php

.
.
.
public function testUpdateTopic()
{
    $topic = $this->makeTopic();

    $editData = ['category_id' => 2, 'body' => 'edit body', 'title' => 'edit title'];

    $response = $this->JWTActingAs($this->user)
        ->json('PATCH', '/api/topics/'.$topic->id, $editData);

    $assertData= [
        'category_id' => 2,
        'user_id' => $this->user->id,
        'title' => 'edit title',
        'body' => clean('edit body', 'user_topic_body'),
    ];

    $response->assertStatus(200)
        ->assertJsonFragment($assertData);
}

protected function makeTopic()
{
    return factory(Topic::class)->create([
        'user_id' => $this->user->id,
        'category_id' => 1,
    ]);
}
.
.
.

我们增加了 testUpdateTopic 测试用例,要修改话题,首先需要为用户创建一个话题,所以增加了 makeTopic,为当前测试的用户生成一个话题。代码与发布话题类似,准备好要修改的话题数据 $editData,调用 修改话题 接口,修改刚才创建的话题,最后断言响应状态码为 200 以及结果中包含 $assertData

执行测试:

$ phpunit


测试查看话题

tests/Feature/TopicApiTest.php

.
.
.
public function testShowTopic()
{
    $topic = $this->makeTopic();
    $response = $this->json('GET', '/api/topics/'.$topic->id);

    $assertData= [
        'category_id' => $topic->category_id,
        'user_id' => $topic->user_id,
        'title' => $topic->title,
        'body' => $topic->body,
    ];

    $response->assertStatus(200)
        ->assertJsonFragment($assertData);
}

public function testIndexTopic()
{
    $response = $this->json('GET', '/api/topics');

    $response->assertStatus(200)
        ->assertJsonStructure(['data', 'meta']);
}
.
.
.

增加了两个测试用户 testShowTopic 和 testIndexTopic,分别测试 话题详情 和 话题列表。这两个接口不需要用户登录即可访问,所以不需要传入 Token。

testShowTopic 先创建一个话题,然后访问 话题详情 接口,断言响应状态码为 200 以及响应数据与刚才创建的话题数据一致。

testIndexTopic 直接访问 话题列表 接口,断言响应状态码为 200,断言响应数据结构中有 data 和 meta

执行测试:

$ phpunit

测试删除话题

tests/Feature/TopicApiTest.php

.
.
.
public function testDeleteTopic()
{
    $topic = $this->makeTopic();
    $response = $this->JWTActingAs($this->user)
        ->json('DELETE', '/api/topics/'.$topic->id);
    $response->assertStatus(204);

    $response = $this->json('GET', '/api/topics/'.$topic->id);
    $response->assertStatus(404);
}
.
.
.

首先通过 makeTopic 创建一个话题,然后通过 DELETE 方法调用 删除话题 接口,将话题删除,断言响应状态码为 204

接着请求话题详情接口,断言响应状态码为 404,因为该话题已经被删除了,所以会得到 404

执行测试:

$ phpunit

最终我们执行了 7 个测试用户,进行了 22 次断言,测试正确。

代码版本控制

$ git add -A
$ git commit -m 'topic test'

三、第三方黑盒测试

除了单元测试以及集成测试之外,还可以利用第三方工具,这一节我们将学习如何利用 PostMan 进行第三方黑盒测试,这也是我们最推崇的测试方案。

第三方黑盒测试的好处是可以最大程度测试 整套系统,我们的 API 接口,从 PHP 代码解析开始,以下涉及因素都会影响到接口的可用性:

  1. 软件代码级别的错误;
  2. 程序使用的第三方软件发生错误,如:Redis 缓存和队列系统、MySQL 数据库等;
  3. API 服务器上的系统软件,如 Nginx、Cron 等;
  4. API 服务器上的物理问题,如硬盘坏了;
  5. 域名解析问题,如 DNS 解析出错;

代码级别的自动化测试,能测试的范围有限。而第三方黑盒测试,模拟的是真实用户的请求,将 API 服务器看成 完全的系统,系统里任何一个部件坏了,都能被检测出来。并且这种测试方法与服务器端环境彻底解耦,后期维护成本较低。

分享接口数据

PostMan 支持我们导出保存的接口,在团队协作中,后端工程师可以方便的将 PostMan 的接口数据分享给客户端工程师,客户端工程师可以自行测试接口,真实的模拟请求。

导出 Collection

有多种导出格式可选,我们选择 PostMan 推荐的 Collection v2.1

选择导出后,就得到了 Larabbs 的接口文件,文件名类似 Larabbs.postman_collection.json

导出环境变量

除了接口数据外,我们还定义了一些环境变量,例如 {{host}}{{jwt_user1}} 等,我们也需要将其导出,分享给他人,这样才是一个完整的环境。

点击右上角的设置,选择 Manage Environments

点击对应环境后面的下载即可。

下载的文件名类似 larabbs-local.postman_environment.json

这里要注意我们现在的环境 larabbs-local 是我们本地的环境,为了方便客户端工程师使用,可以在某个线上可访问的测试服务器搭建完整的 Larabbs 环境,增加测试服务器的环境 larabbs-test,设置测试环境的环境变量 {{host}}{{jwt_user1}} 等,这样分享出去的环境,别人可以直接使用。

导入 Collection 及环境

我们将导出的 Larabbs.postman_collection.json 和 larabbs-local.postman_environment.json 两个文件分享给客户端的工程师。

点击左上角的 Import 即可导入 Collection 文件。

在 Manage Environments 中点击 Import 即可导入环境变量。

导入成功后,我们就可以直接对接口进行真实调试。

PostMan 自动化测试

PostMan 为我们提供了自动化测试的功能,类似于 Laravel 的接口测试,PostMan 可以请求接口,并且断言响应结果和响应数据,接下来我们以 发布话题 和 话题列表 两个接口为例,进行自动化测试。

测试发布话题

打开 发布话题 接口,可以看到有个 Tests 的选项卡,点击该选项卡会出现一个空白区域,在这里我们可以添加一些断言,判断请求结果。

填入如下内容:

pm.test("响应状态码正确", function () { 
    pm.response.to.have.status(201);
});

pm.test("接口响应数据正确", function () { 
    pm.expect(pm.response.text()).to.include("id");
    pm.expect(pm.response.text()).to.include("title");
    pm.expect(pm.response.text()).to.include("body");
    pm.expect(pm.response.text()).to.include("user_id");
    pm.expect(pm.response.text()).to.include("category_id");
});

PostMan 为我们提供了 pm.test 方法,相当于一个测试用例,第一个参数是执行正确后的提示文字,第二个参数是个闭包,执行我们的断言。

第一个测试用户我们判断响应的状态码,pm.response.to.have.status(201); 断言响应结果的状态码是 201

第二个测试用户,我们判断响应数据,通过 pm.expect(pm.response.text()).to.include(""); 断言响应数据中一定会包含某个字段。

点击 Send 进行调试。

切换到 Test Results,可以看到两个测试用例均通过了。

测试话题列表

同样的,我们为 话题列表 接口增加测试用例:

// example using response assertions
pm.test("响应状态码正确", function () { 
    pm.response.to.have.status(200);
});

pm.test("接口响应数据正确", function () { 
    pm.expect(pm.response.text()).to.include("data");
    pm.expect(pm.response.text()).to.include("meta");
});

同样增加了两个测试用例,断言响应状态码为 200,断言响应数据中包含 data 和 meta

点击 Send 进行调试。

测试通过。

批量测试

每个接口都完成测试用户后,我们就可以通过 PostMan 的测试工具进行自动化测试了。

点击 PostMan 左上角的 Runner,我们可以看到 PostMan 的自动化测试界面,我们可以选择测试整个项目,或者测试某个目录。这里我们选择 话题 目录,选择 larabbs-local 环境,执行测试。

我们可以看到 PostMan 依次请求了 话题 目录下的所有接口,因为我们为 发布话题 和 话题列表 添加了测试用例和断言,所以看到这两个接口的测试用户均已通过。

我们可以为所有的接口增加测试用例,这样当接口升级之后,可以方便的通过 PostMan 的自动化测试工具进行测试,快速定位不符合预期的接口。

四、API文档

完成了所有的 API 及测试,我们需要有个一份接口文档,方便他人使用,这一节我们来介绍一下快速生成 API 文档的方法。

PostMan

PostMan 导出的 Collection 其实已经是一份基础的接口文档,可以进一步补充请求参数的描述信息。

当然也有很多缺点:

  • 需要通过导入导出来分享文档;
  • 没法对响应结果进行详细说明;
  • 没有地方添加关于当个接口的进一步说明;

当然了付费用户可以享受更多方便的功能,我们在这里不做进一步讨论。

Apizza 介绍

Apizza (apizza - 极客专属的api管理工具)是一款在线的 API 协作管理工具,界面及使用方式与 PostMan 基本类似,可以理解为一款在线版的 PostMan。

但是与 PostMan 相比,功能要更加的丰富,比如我们可以更加详细的定义请求参数和参数类型。

可以更加详细的描述响应数据。

支持 Markdown 格式的说明文件,我们可以为一组接口增加详细的调用说明。

另外 Apizza 还支持直接导入 PostMan 的 Collection 文件。

注意目前只支持 PostMan v1 版本的 Collection 文件。从 PostMan 导出一份 v1 版本的文件,导入 Apizza 中。

导出成功后即可看到文档已经同 PostMan 保持一致了。

当然还有团队协作以及文档分享功能。

总结

开发阶段我们一定会使用 PostMan 进行接口调试,将 PostMan 的 Collection 文件导入 Apizza,然后再与团队成员一起进一步的完善文档,最后分享给他人使用,是一件方便快速的事情。

虽然 APIdoc 和 swagger 都是十分优秀的文档工具,但是依然有一定的学习成本和维护成本。所以我们更加推荐 PostMan 与 Apizza 这样的在线工具结合使用,快速的完成 API 文档。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Laravel是一种流行的PHP框架,用于构建Web应用程序。RBAC(Role-Based Access Control)是一种在应用程序中管理用户权限的方法。在Laravel中,可以使用API代码来实现RBAC。 实现RBAC的第一步是定义角色和权限。在Laravel中,可以使用数据库表来存储角色和权限的信息。可以创建一个名为roles的表来存储角色信息,包括角色名称和可访问的路由。还可以创建一个名为permissions的表来存储权限信息,包括权限名称和对应的路由。 接下来,需要在Laravel中定义路由,用来处理权限的验证和角色的分配。可以创建一个名为'check.permission'的中间件,用于验证用户的权限。在这个中间件中,可以查询数据库,获取当前用户的角色和可访问的路由,然后进行权限验证。如果用户没有权限访问该路由,则返回相应的错误信息。 当用户登录后,可以为用户分配一个或多个角色。在Laravel中,可以使用Session来存储用户信息。可以创建一个名为'assign.role'的路由,用于接收用户角色的分配请求。在这个路由中,可以将用户ID和角色ID存储到数据库中,并将用户的角色信息存储到Session中。 最后,可以创建一个名为'check.role'的中间件,用于验证用户的角色。在这个中间件中,可以查询数据库,获取当前用户的角色信息,并与当前访问的路由所需的角色进行比较。如果用户不具备所需的角色,则返回相应的错误信息。 通过以上步骤,可以实现基于角色和权限的访问控制。在Laravel中使用RBAC API代码,可以有效地管理和控制用户的权限,确保应用程序的安全性和完整性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值