프로그래밍/PHP

PHPUnit를 이용한 외부 REST API 단위 시험

채윤아빠 2019. 5. 22. 20:27
728x90
반응형



PHPUnit의 설치

PHPUnit은 PHP 프로젝트의 단위 시험(Unit Test) 도구로 널리 사용되고 있습니다.


PHPUnit을 설치하는 방법으로는 PHP 아카이브(phar 파일)을 다운로드하여 직접 실행하는 방법이 있고, PHP 패키지 및 의존성 관리 도구인 컴포저(Composer)를 이용하는 방법이 있습니다.


phar로 설치하는 방법

Windows 10

PHPUnit 공식 사이트 https://phar.phpunit.de/ 에서 이용중인 PHP 버전에 맞는 PHPUnit의 아카이브 파일(*.phar)을 다운로드 받습니다.


PHP 7.0.x ~ 7.2.x : phpunit-6.x.phar
PHP 7.1.x ~ 7.3.x : phpunit-7.x.phar
PHP 7.2.x ~ 7.4.x : phpunit-8.x.phar


이용중인 PHP 버전에 맞는 phpunit*.phar 파일을 다운로드 받은 후에, 해당 파일을 "PHP.exe"가 위치해 있는 폴더에 복사하고, 그 폴더에 "phpunit.bat" 파일을 다음과 같이 작성합니다.

@ECHO OFF
php.exe "%~dp0phpunit-6.5.14.phar" %*

"phpunit.bat" 파일이 실행될 수 있도록 해당 경로를 환경설정의 "path"에 추가합니다.

커맨드 창을 연 후에, "phpunit" 명령을 입력하였을 때, 아래와 같이 출력되면 설치가 성공한 것입니다.

PS D:\Work\API\wapi\v1.0> phpunit
PHPUnit 6.5.14 by Sebastian Bergmann and contributors.

Usage: phpunit [options] UnitTest [UnitTest.php]
       phpunit [options] <directory>

Code Coverage Options:

...

PHPUnit을 이용한 REST API 단위 시험 작성

다음과 같이 PHPUnit을 이용한 단위 시험(Unit Test) 소스를 작성합니다.

TestCase는 "ApiTestHelper::api_get()" 메소드를 이용하여 특정 REST API의 반환값을 얻어오고, 그 결과로 예상되는 것을 비교하여 단위 시험을 진행하도록 작성합니다. 아래 소스에서는 API_SITE_LIST 에 대하여 필수 파라미터가 없는 경우 및 찾을 수 없는 항목의 키 값을 넘겼을 때 예상되는 결과대로 반환이 되는지 확인하는 TestCase입니다.

<?php
use PHPUnit\Framework\TestCase;

require "ApiTestHelper.php";

class WapiTest extends ApiTestHelper
{
    const PROTOCOL       = "http://";

    const WAPI_HOST      = "api.REST.com";

    const API_SITE       = "/wapi/v1.0/site";
    const API_SITE_LIST  = "/wapi/v1.0/site_list";
    const API_TOKEN      = "/wapi/v1.0/token";
    const API_ADMIN      = "/wapi/v1.0/admin";

    public function testSiteList_NoParam1()
    {
        $result = $this->api_get(self::WAPI_HOST, self::API_SITE_LIST);

        $this->assertTrue($result['response_code'] === 200);
        $this->assertSame('{"error_code":1, "msg":"Need <company_no> parameter"}', $result['data']);
    }

    public function testSiteList_NotFound()
    {
        $result = $this->api_get(self::WAPI_HOST, self::API_SITE_LIST . "/120312039102934");

        $this->assertTrue($result['response_code'] === 200);
        $this->assertSame('{"error_code":0, "msg":"Success", "data":[]}', $result['data']);
    }
}

"ApiTestHelper.php" 소스는 다음과 같이 작성합니다.

<?php
/**
 * @brief   API를 시험하는데 사용하는 GET / POST / PUT / DELETE 메소드를 시험하는데 필요한
 *          기본 메소드를 정의한 클래스
 * @author  hbesthee@naver.com
 * @date    2019-05-20
 */

use PHPUnit\Framework\TestCase;

class ApiTestHelper extends TestCase
{
    protected $_protocol = "http://";

    /**
     * API 서버에 curl을 이용한 GET 처리를 수행하는 함수입니다.<br /><br />
     * API 서버에서 요청에 대한 응답받은 내용을 그대로 반환합니다.
     *
     * @package         ApiTestCase
     * @subpackage      TestCase
     * @category        TestCase
     * @author          hbesthee@naver.com
     * @date            2019-05-20
     * @license         hbesthee (c)
     * @param $host string API 서버 Host 이름
     * @param $uri string API 서버에 요청할 API URI ; "?" 전까지로 '/' 문자로 시작해야 합니다.
     * @param $params string URL에 추가될 파라미터 목록 문자열
     */
    public function api_get($host, $uri, $params = '')
    {
        $url = $this->_protocol . $host . $uri;
        if (!empty($params) && !is_null($params))
            $url .= '?' . $params;

        // print($url);
        $request_headers = array();
        $request_headers[] = 'Content-Type:application/json';

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);

        // SSL 인증서 날짜 만료 등에 대한 예외 무시
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

        // 추가 헤더
        curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);

        $data = curl_exec($ch);

        $response_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        curl_close($ch);

        $result = array(
            "response_code" => $response_code
            , "data" => $data
        );

        return $result;
    }
}

위 단위 시험(Unit Test) 소스를 phpunit로 실행하면 다음과 같은 결과가 나옵니다.

PHPUnit 6.5.14 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 197 ms, Memory: 10.00MB

OK (2 tests, 4 assertions)

위 예제 소스들은 단위 시험(Unit Test)을 위한 일부의 소스만 있습니다. REST API의 POST, PUT, DELETE 모두 시험하기 위해서는 "ApiTestHelper::api_get()" 메소드와 유사하게 "ApiTestHelper::api_post()", "ApiTestHelper::api_put()", "ApiTestHelper::api_delete()" 등의 메소드를 작성하고, 작성된 메소드들을 이용하여 각 기능별 TestCase를 작성합니다.


단위 시험(Unit Test)을 작성할 때는 시험 절차를 잘 고려하여 작성해야 사전에 문제를 확인하고 수정할 수 있습니다. 예를 들어, GET으로 파라미터 없는 경우 및 원하는 데이터를 찾지 못하는 조건으로 위와 같이 TestCase를 작성하여 예상되는 결과가 나타나는지 확인하도록 합니다. 이후 POST로 새로운 데이터를 입력할 때, 필수 항목 누락시 API의 예상된 오류 처리들을 확인하고, 정상적으로 데이터를 입력했을 때 예상되는 결과가 맞게 나오는지 확인합니다. 이후 새로 추가된 데이터를 GET으로 조회하여 추가한 데이터가 정상적으로 반환되는지 확인하고, PUT으로 데이터를 수정합니다. 수정 후, GET으로 수정된 데이터가 정상적으로 반영되었는지 확인하고, 마지막으로 단위 시험을 위하여 추가했던 데이터를 DELETE로 삭제하고, GET으로 실제 추가했던 데이터가 삭제되어 찾을 수 없다는 결과가 맞게 나오는지 확인합니다.


이런 식으로 데이터 "조회 > 삽입 > 조회 > 수정 > 조회 > 삭제 > 조회" 등으로 시나리오를 구성하여 전반적으로 데이터 처리에 이상이 없는지 단위 시험으로 검증하도록 합니다. 이러한 과정을 사람이 일일이 수작업으로 한다면, 엄청난 시간과 노력이 필요하지만 PHPUnit으로는 한 번의 명령 수행으로 손쉽게 검증이 가능합니다.



VS Code에서 PHPUnit 연동

VS Code에서 "Debug" 탭을 선택하고, "DEBUG"에서 "Add Configuration..." 메뉴를 선택하여 "launch.json" 파일에 아래와 같이 PHPUnit를 구동할 수 있도록 설정을 추가합니다.

        {
            "name": "Launch PHPUnit",
            "type": "php",
            "request": "launch",
            "program": "${fileDirname}",
            "cwd": "${fileDirname}",
            "runtimeExecutable": "C:\\phpunit.bat-path\\phpunit.bat",
        }

위와 같이 설정을 추가한 후, "DEBUG"에서 위에서 추가한 "Launch PHPUnit"로 선택해 놓은 상태에서 PHPUnit을 수행할 폴더에 있는 소스를 선택 후, 디버그 시작(F5) 또는 디버그 없이 시작(Ctrl + F5)를 선택하면, PHPUnit가 프로젝트에 대하여 자동으로 실행되고 그 결과가 "DEBUG CONSOLE"에 표시됩니다.

위 설정에서 "${fileDirname}"은 해당 TestCase php 파일이 있는 폴더 전체에 대하여 단위 시험을 수행하는 것이고, 파일 개별적으로 수행하고 싶을 경우에는 "program" 항목을 "${file}"로 수정하면 됩니다.



결론

프로젝트를 정식으로 오픈하는데 각종 시험과 그에 따른 오류 수정에 상당히 많은 시간이 필요합니다. 하지만 단위 시험을 꼼꼼히 TestCase를 적용하여 사전에 검증을 완료해 놓는다면 그 시간을 상당히 줄일 수 있습니다.


단위 시험은 프로젝트가 완료되고, 사이트가 공개 되었다고하여 동일한 TestCase로 시험을 해서는 안됩니다. 사이트 공개 이후에도 각종 신규 기능이 추가되고 미처 알지 못했던 버그도 수정하게 됩니다. 단위 시험을 위한 TestCase도 신규 기능 및 버그에 맞추어 계속 수정이 되어야만 합니다.


PHP 프로젝트에서도 PHPUnit를 이용하여 단위 시험을 꼼꼼히 한다면 프로젝트를 좀 더 수월하게 완료할 수 있을 것으로 기대합니다.



참고자료