こなメル

学習したことのメモや、趣味について

PHPでテストを書く(PHPUnit)

自動テストの重要性

コードを書き換えた後、再テストには大きな労力がかかる。 => 自動テストという仕組みが役に立つ。

参考書籍に則り、PHPUnitという自動テストツールを使用する。

他にも様々なツールがあるみたい。

PHPの自動テストフレームワークってどんなんがあるん?

PHPUnitのインストール

PHPUnitのインストール

composer require --dev phpunit/phpunit

テストの書き方

  • PHPUnitに用意されてる TestCaseを継承したクラスを作成する必要がある。
  • クラス名、ファイル名はTestで終わる必要がある。
  • テストの成功か、失敗かはメソッド内に書かれたassertOOメソッドが真を返すかどうかで決まる。

実行

ファイル名: MyTest.php

<?php
require_once dirname(__FILE__) . '/vendor/autoload.php';

use PHPUnit\Framework\TestCase;

class MyTest extends TestCase
{
  // テスト処理
  public function test1()
  {
    $array = [1, 5, 10];

    // 配列($array)の要素数が3つであることをテストする。
    $this->assertCount(3, $array);
  }

  public function test2()
  {
    $num1 = 1;
    $num2 = 1;

    // 2つの値が型も含めて等しいことをテストする。
    $this->assertSame($num1, $num2);
  }

  public function test3()
  {
    $text = "hello";

    // 値が空であることをテストする。
    $this->assertEmpty($text);
  }
}

実行

./vendor/phpunit/phpunit/phpunit MyTest.php

実行結果

PHPUnit 9.5.0 by Sebastian Bergmann and contributors.

..F                                                                 3 / 3 (100%)

Time: 00:00.008, Memory: 4.00 MB

There was 1 failure:

1) MyTest::test3
Failed asserting that a string is empty.

/Users/kona/PHPUnitApp/MyTest.php:31

FAILURES!
Tests: 3, Assertions: 3, Failures: 1.

↑の実行結果は2つが成功し、1つが失敗している。

2行目が結果を示している。

  • 「.」が成功
  • 「F」が失敗
  • 「E」がエラー
  • 「R」がリスク
  • 「S」がスキップ
  • 「I」が不完全

おまけ(エイリアスの作成)

エイリアスを作成して楽しよう

alias phpunit="./vendor/phpunit/phpunit/phpunit"

以降、

phpunit MyTest.php

で実行可能。

参考情報

PHPの自動テストフレームワークってどんなんがあるん?

簡単なRESTful APIサーバーを作ってみる

RESTful APIって?

標準的なWebAPIの設計方式。
リソースに対するURLを1つだけ用意し、HTTPメソッドの切り替えで操作を表す。

RESTful APIはステートレスであるべき

ステートレス = WebAPIサーバはセッション変数を持つべきではない。
サーバ間で共有ができなくなる。などの問題があるから。
スケーラビリティの問題。(今後サーバを増やしたいときとか)

PHPでRESTfulAPIサーバ、クライアントの作成

環境

macOS BigSur 11.0.1

PHP 7.3.22

MAMP 5.7

サーバサイドのPHPファイルを作成

<?php
declare(strict_types=1);

// GET
function getMessage()
{
  $res = [
    'status' => 'success',
    'message' => 'ユーザ: ' . $_GET['userName'] . 'の好きな食べ物は、' . $_GET['food'] . 'です。',
  ];
  return $res;
}

//POST
function postMessage()
{
  $res = [
    'status' => 'success',
    'message' => 'ユーザ: ' . $_POST['userName'] . 'の好きな食べ物を、' . $_POST['food'] . 'に登録しました。',
  ];
  return $res;
}

// PUT
function putMessage()
{
  parse_str(file_get_contents('php://input'), $putRequest);
  $res = [
      'status' => 'success',
      'message' => 'ユーザ: ' . $putRequest['userName'] . 'の好きな食べ物を、 ' . $putRequest['food'] . 'に変更しました。',
  ];
  return $res;
}

// DELETE
function deleteMessage()
{
  parse_str(file_get_contents('php://input'), $deleteRequest);
  $res = [
      'status' => 'success',
      'message' => 'ユーザ: ' . $deleteRequest['userName'] . 'の好きな食べ物、' . $deleteRequest['food'] . 'を削除しました。',
  ];
  return $res;
}

switch (strtolower($_SERVER['REQUEST_METHOD'])) {
  case 'get':
    echo json_encode(getMessage());
    break;
  case 'post':
    echo json_encode(postMessage());
    break;
  case 'put':
    echo json_encode(putMessage());
    break;
  case 'delete':
    echo json_encode(deleteMessage());
    break;
}

クライアントサイドのPHPファイルを作成

cUEL関数を使用。

  1. curl_init() でURLを指定。cURLハンドルを取得。
  2. curl_setopt() でオプションの指定。
  3. curl_exec() でハンドルを渡し、リクエストを送信。レスポンスを取得。
  4. curl_close() でハンドルを閉じる。

GET

<?php
declare(strict_types=1);

$params = [
    'userId' => 1001,
    'userName' => "kona",
    'food' => '抹茶',
];

$url = "http://localhost:8888/server.php?" . http_build_query($params);

$handle = curl_init($url);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
$apiResponse = json_decode(curl_exec($handle), true);
curl_close($handle);

echo "サーバからの応答:\n";
print_r($apiResponse);

実行結果

サーバからの応答:
Array
(
    [status] => success
    [message] => ユーザ: konaの好きな食べ物は、抹茶です。
)

http_build_query()連想配列をパラメータ文字列へ。

$params = [
        'userId' => 1001,
        'userName' => "kona",
        'food' => '抹茶',
    ];

userId=1001&userName=kona&food=抹茶(Unicodeに変換される)

POST

<?php
declare(strict_types=1);

$params = [
    'userId' => 1001,
    'userName' => "kona",
    'food' => '抹茶',
];

$opt = [
    CURLOPT_URL => 'http://localhost:8888/server.php',
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => $params,
    CURLOPT_RETURNTRANSFER => true,
];

$handle = curl_init();
curl_setopt_array($handle, $opt);
$apiResponse = json_decode(curl_exec($handle), true);
curl_close($handle);

echo "サーバからの応答:\n";
print_r($apiResponse);

実行結果

サーバからの応答:
Array
(
    [status] => success
    [message] => ユーザ: konaの好きな食べ物を、抹茶に登録しました。
)

curl_setopt_array()で配列で指定した設定を適応できる。

PUT

<?php
declare(strict_types=1);

$params = [
    'userId' => 1001,
    'userName' => "kona",
    'food' => '抹茶アイス',
];

$opt = [
    CURLOPT_URL => 'http://localhost:8888/server.php',
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_POSTFIELDS => http_build_query($params),
    CURLOPT_RETURNTRANSFER => true,
];

$handle = curl_init();
curl_setopt_array($handle, $opt);
$apiResponse = json_decode(curl_exec($handle), true);
curl_close($handle);

echo "サーバからの応答:\n";
print_r($apiResponse);

実行結果

サーバからの応答:
Array
(
    [status] => success
    [message] => ユーザ: konaの好きな食べ物を、 抹茶アイスに変更しました。
)

DELETE

<?php
declare(strict_types=1);

$params = [
    'userId' => 1001,
    'userName' => "kona",
    'food' => '抹茶アイス',
];

$opt = [
    CURLOPT_URL => 'http://localhost:8888/server.php',
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_POSTFIELDS => http_build_query($params),
    CURLOPT_RETURNTRANSFER => true,
];

$handle = curl_init();
curl_setopt_array($handle, $opt);
$apiResponse = json_decode(curl_exec($handle), true);
curl_close($handle);

echo "サーバからの応答: \n";
print_r($apiResponse);

実行結果

サーバからの応答: 
Array
(
    [status] => success
    [message] => ユーザ: konaの好きな食べ物、抹茶アイスを削除しました。
)

参考書籍