Laravel Chapter 10 API Testing and Documentation

1. Introduction to API automated testing

API automated testing

We have completed all the interface development of Larabbs, and then we need to deliver the interface to other engineers for docking. When deploying the interface to the production environment, how to ensure that the delivered interface is correct and stable? In the future, we will add new functions to the project. At that time, how can we ensure that the interface is still normal after the code is upgraded?

During the development process, we use PostMan to manually test the interfaces one by one, but when we have dozens or even hundreds of interfaces, manual testing will not be applicable if we want to test these interfaces at the same time. The solution is automated testing. Automated testing is an important part of ensuring project quality. In this section, let's understand the related concepts of testing.

unit test

Unit testing refers to the inspection and verification of the smallest testable unit in the software. For PHP, it usually  tests a certain method in a certain class , or a single method. The purpose of unit testing is to first ensure that the  basic components of a system  can work normally, and all basic parts work normally, so that the assembled software will not have problems.

Unit testing is  code-level  testing, and the development and maintenance costs are high, so it is not recommended for small teams. If it is multi-person collaboration, my task is to separately encapsulate some common functions, such as writing a Service; or write an extension package to provide some underlying code interfaces, such as packaging an SDK for a third-party application, then unit testing is very useful. necessary.

There are many knowledge points involved in unit testing. In this tutorial, we will not explain unit testing separately.

API integration testing

The integrated API test that comes with the Laravel framework, we initialize the complete application context, and after preparing the test data in the database, we can easily simulate various request methods, call the interface to obtain the response result, and finally assert whether the returned result is equal to the  expected  result result.

API  integration tests  need to prepare test data, set users, assign user permissions, and prepare associated data for the test interface. Although there is a certain maintenance cost, compared with unit testing, the maintenance cost is much less, and to a certain extent, it can better ensure the robustness of the project.

PostMan test

Use PostMan and other tools for manual testing. This is the most recommended testing solution. It is very suitable for small teams, because it can simulate user requests more realistically, and in team collaboration, back-end engineers can share PostMan's debugging interface with clients. Engineer, if there is a problem with the interface, the client engineer can test it by himself.

2. Laravel API integration test

Create a new folder Go to the parent directory of the new folder, $mkdir folder name

In this section we learn about API integration testing through several examples.

If you are using or upgraded to Laravel 5.7, the version of phpunit should be 7.*

PHPUnit

PHPUnit is a lightweight PHP testing framework. Laravel supports PHPUnit for testing by default, and configures the phpunit.xml file for your application. You only need to run it on the command line to test  phpunit .

There is a small error in the current Larabbs project, which will cause an error when running phpunit  PHP Fatal error: Cannot redeclare route_class(). Just because there are some problems with the introduction of our custom method, modify it slightly:

bootstrap/app.php

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

The previous code used to  require introduce a custom method, which may be introduced repeatedly, so it is changed here  require_once.

Try to execute in larabbs root directory phpunit

$ phpunit


Create test file

First you need to create a test file:

$ php artisan make:test TopicApiTest

This command will  tests/Feature create  TopicApiTest.php files in the directory. We will find that there are two directories in the tests directory  Feature .  Unit How to distinguish these two directories?

  • Unit - Unit tests are written from a programmer's point of view. They are used to ensure that a particular method of a class performs a specific set of tasks.
  • Feature - Functional tests are written from the user's point of view. They ensure that the system behaves as the user expects, including the interaction of several objects, or even a complete HTTP request.

Our test API is a functional test and should be created in  Feature the directory.


test release topic

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 method will be executed before the test starts, we first create a user, and the test will test as this user.

testStoreTopic It is a test user, testing and posting topics. Using $this->json can conveniently simulate various HTTP requests:

  • The first parameter - the method of the request, the POST method is used to publish the topic;
  • The second parameter - request address, request  /api/topics;
  • The third parameter - request parameters, incoming  category_id, body, title, these three required parameters;
  • The fourth parameter - Request Header, can be set directly  header, or use  withHeaders methods to achieve the same purpose;

We found that   the code  generated Token and set up  for the user will not only  be used, but also the test cases of other functions written in the future will also be used, so let's encapsulate it.Authorization修改话题删除话题

Add a 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;
    }
}

Modify the test case to use 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);
    }
}

In this way, we can easily use  JWTActingAs the method to log in a user. The resulting response  $response, by  assertStatus asserting that the response result is  201assertJsonFragment contains  assertData data by asserting that the response result is.

Execute the test:

$ phpunit

The card keeps reporting an error here: Error: Call to undefined method Illuminate\Auth\TokenGuard::fromUser()

waiting to be resolved


Test modification topic

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,
    ]);
}
.
.
.

We have added  testUpdateTopic a test case. To modify the topic, we first need to create a topic for the user, so we have added  makeTopica topic to generate a topic for the currently tested user. The code is similar to publishing a topic, prepare the topic data to be modified  $editData, call  修改话题 the interface, modify the topic just created, and finally assert that the response status code is  200 and the result contains it  $assertData.

Execute the test:

$ phpunit


test view topic

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']);
}
.
.
.

Added two test users  testShowTopic and  , test  and  testIndexTopicrespectively  . These two interfaces can be accessed without user login, so there is no need to pass in Token.话题详情话题列表

testShowTopic First create a topic, then access  话题详情 the interface, assert that the response status code is  200 and the response data is consistent with the topic data just created.

testIndexTopic Directly access  话题列表 the interface, the status code of the assertion response is  , and there are  and  200in the assertion response data structure  .datameta

Execute the test:

$ phpunit

Test delete topic

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);
}
.
.
.

First  makeTopic create a topic, then  DELETE call  删除话题 the interface through the method to delete the topic, and assert that the response status code is  204.

Then request the topic details interface, and assert that the response status code is  404, because the topic has been deleted, so it will be obtained  404.

Execute the test:

$ phpunit

In the end, we executed 7 test users, made 22 assertions, and the test was correct.

code version control

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

3. Third-party black box testing

In addition to unit testing and integration testing, third-party tools can also be used. In this section, we will learn how to use PostMan for third-party black-box testing, which is our most recommended testing solution.

The advantage of third-party black-box testing is that it can test the entire system to the greatest extent  . For our API interface, starting from PHP code analysis, the following factors will affect the usability of the interface:

  1. Errors at the software code level;
  2. Errors occur in third-party software used by the program, such as: Redis cache and queue system, MySQL database, etc.;
  3. System software on the API server, such as Nginx, Cron, etc.;
  4. Physical problems on the API server, such as hard disk failure;
  5. Domain name resolution problems, such as DNS resolution errors;

Automated testing at the code level has a limited scope of testing. The third-party black-box test simulates the requests of real users, and regards the API server as a  complete system . Any broken part in the system can be detected. Moreover, this test method is completely decoupled from the server-side environment, and the later maintenance cost is low.

Share interface data

PostMan supports us to export the saved interface. In team collaboration, the back-end engineer can easily share the interface data of PostMan with the client engineer, and the client engineer can test the interface by himself and simulate the real request.

Export Collection

There are many export formats to choose from, we choose the one recommended by PostMan  Collection v2.1.

After choosing to export, you will get  Larabbs the interface file with a similar file name  Larabbs.postman_collection.json.

export environment variables

In addition to the interface data, we also define some environment variables, such as  { {host}}, { {jwt_user1}} etc., we also need to export them and share them with others, so that it is a complete environment.

Click Settings in the upper right corner, select  Manage Environments.

Click the download behind the corresponding environment.

The downloaded filenames are similar  larabbs-local.postman_environment.json.

It should be noted here that our current environment  larabbs-local is our local environment. In order to facilitate the use of client engineers, we can build a complete Larabbs environment on an online accessible test server, increase the environment of the test server, and  larabbs-testset the environment variables of the test environment  { {host}}. { {jwt_user1}} Wait, the environment shared in this way can be used directly by others.

Import Collection and environment

We will share the exported  Larabbs.postman_collection.json and  larabbs-local.postman_environment.json two files with the client's engineers.

Click on the upper left corner  Import to import Collection files.

Manage Environments Click in  to  Import import environment variables.

After the import is successful, we can directly debug the interface.

PostMan automated testing

PostMan provides us with the function of automated testing, similar to Laravel's interface testing, PostMan can request the interface, and assert the response result and response data, and then we will take the  发布话题 two  话题列表 interfaces as an example to conduct automated testing.

test release topic

Open  发布话题 the interface, you can see a  Tests tab, click this tab, a blank area will appear, here we can add some assertions to judge the result of the request.

Fill in the following content:

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 provides us with  pm.test a method, which is equivalent to a test case. The first parameter is the prompt text after the execution is correct, and the second parameter is a closure to execute our assertion.

For the first test user, we judge the status code of the response and pm.response.to.have.status(201); assert that the status code of the response result is  201.

For the second test user, we judge the response data by  pm.expect(pm.response.text()).to.include(""); asserting that the response data must contain a certain field.

Click  Send to debug.

Switch to  Test Results, and you can see that both test cases have passed.

List of test topics

Similarly, we  话题列表 add test cases for the interface:

// 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");
});

Two test cases are also added, asserting that the response status code is  200, and asserting that the response data contains  data and  meta.

Click  Send to debug.

The test passes.

batch test

After completing the user test for each interface, we can conduct automated testing through PostMan's testing tools.

Click on the upper left corner of PostMan  Runner, we can see the automated test interface of PostMan, we can choose to test the entire project, or test a certain directory. Here we select  话题 the directory, select  larabbs-local the environment, and execute the test.

We can see that PostMan requests  话题 all the interfaces in the directory in turn, because we   added test cases and assertions for 发布话题 and  , so we can see that the test users of these two interfaces have passed.话题列表

We can add test cases for all interfaces, so that after the interface is upgraded, it can be easily tested through PostMan's automated testing tool to quickly locate the interface that does not meet expectations.

4. API documentation

After completing all the APIs and tests, we need to have an interface document for the convenience of others. In this section, we will introduce the method of quickly generating API documents.

PostMan

The Collection exported by PostMan is already a basic interface document, which can further supplement the description information of the request parameters.

Of course there are many disadvantages:

  • Need to share documents by importing and exporting;
  • It is impossible to describe the response results in detail;
  • There is no place to add further explanation about the interface;

Of course, paid users can enjoy more convenient functions, we will not discuss further here.

Introduction

Apizza  ( apizza - an api management tool for geeks ) is an online API collaboration management tool. The interface and usage are basically similar to PostMan, which can be understood as an online version of PostMan.

But compared with PostMan, the functions are more abundant, for example, we can define request parameters and parameter types in more detail.

The response data can be described in more detail.

It supports description files in Markdown format, and we can add detailed call descriptions for a set of interfaces.

In addition, Apizza also supports direct import of PostMan Collection files.

Note that currently only Collection files of PostMan v1 are supported. Export a v1 file from PostMan and import it into Apizza.

After the export is successful, you can see that the document has been consistent with PostMan.

Of course, there are also team collaboration and document sharing functions.

Summarize

In the development stage, we will definitely use PostMan for interface debugging, import PostMan Collection files into Apizza, and then further improve the documents with team members, and finally share them with others, which is a convenient and fast thing.

Although  both APIdoc  and  swagger  are excellent documentation tools, they still have a certain learning cost and maintenance cost. Therefore, we recommend PostMan to be used in combination with online tools like Apizza to quickly complete API documentation.

Guess you like

Origin blog.csdn.net/jiangyangll/article/details/89702946