메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

데이터로서의 코드: PHP의 Reflection(2)

한빛미디어

|

2007-06-05

|

by HANBIT

9,692

제공 : 한빛 네트워크
저자 : Zachary Kessin
역자 : 유일호
원문 : Code As Data: Reflection in PHP

코드 생성

일단 테스트 케이스들이 사용자 인터페이스를 통해 생성이 되면, 실제로 코드를 만들어 내는 일만 남는다. 이것은 각 테스트를 위한 코드를 정립하고, 그것을 실행한다는 관점에 보면 매우 단순한 과정이다. 만약에 메소드가 예상된 결과를 리턴하면 테스트는 통과 하겠지만 그렇지 않다면 실패하게 된다. 테스트들은 테스트 프레임웍에 의해서 순서대로 실행된다. 각 테스트마다, 오브젝트를 생성하고, 메소드를 호출하거나, 결과를 검사할 간단한 함수가 반드시 생성되어야 할 것이고, 그 후에 필요한 모든 클린업(cleanup)을 하도록 해야 한다. 각각의 테스트는 파라미터들이 없는 간단한 함수일 것이다. 테스트 함수들은 PASS나 FAIL을 리턴할 것이다.

테스트들은 명령라인(command-line) 도구에 의해 구축되어 진다. 도구는 테스트들이 생성될 수 있도록 XML파일을 읽고, 테스트들이 포함된 PHP파일을 내보내며, 테스트 실행(runner) 스크립트와 함께 동작될 것이다.

각각의 테스트는 파라미터들이 없는 PHP함수로 구현되었다. 테스트 실행 스크립트는 그것들의 함수목록을 얻을 것이고, 순서대로 실행하게 될 것이다. 만약에 테스트가 통과된다면 “PASS” 상수를 리턴하게 될 것이고, 만약에 실패한다면 “FAIL”상수를 리턴하게 될 것이다. 테스트 실행 스크립트는 모든 테스트들을 반복할 수 있고, 사용자에게 데이터를 리턴 할 수도 있다. 기본적으로, 그것은 테스트이름 뒤에 “PASS” 나 “FAIL” 이 따라오는 형태로 명령라인(command line)에서 실행될 것이다. 이것은 또한 각각의 테스트가 얼마나 실행되었는지 log를 남길 때 아주 유용할 것이다.

각 테스트의 기본적인 형태는 클래스 생성자를 호출 하는 것이며(주어진 어떤 파라미터들과 함께), 생성자가 올바른 타입의 유효한 오브젝트를 리턴 했는지 체크 하는 것이다. 만약에 그렇게 되지 않는다면, 테스트는 실패할 것이다(그것이 기대한 결과가 아니라면). 테스트는 어썰션들이 참인지를 체크하게 될 것이다.

어썰션 가능한 세가지 형태의 타입들이 있다. 첫번째는 리턴 값으로, 참이나 거짓, 널(null), 문자열, 숫자 값, 특정 결과나 다른 어떤 결과 들이 PHP 함수에 의해 리턴될 수 있을 것이다. 이것들 중 어느 것이라도 테스트를 위해 사용할 수 있을 것이다. 테스트는 리턴값이 예상된 결과라면 그 값은 PASS 일것이고, 아니라면 FAIL 일 것이다.

두번째 타입은 클래스 값이 할당되어 있는지 검사 하는 것이다. 클래스 프로퍼티 어썰션은 리턴값 어썰션에서와 같이 동일한 형태로 테스트를 할 수 있다. 경우에 따라서, 단일 클래스 프로퍼티 어썰션이나 리턴값 어썰션, 그리고 프로퍼티 어썰션이 하나 이상이 있어야 할 것이다. 이런 경우에는 그것들은 서로가 만족해야만 한다.

세번째 형태의 어썰션은 예외(exception)이다. 이것은 일반적인 예외나 특정 형태의 예외일수 있을 것이다. 이 경우에는, 예상된 예외가 일어난다면 테스트가 통과될 것이고, 그 외에는 실패할 것이다.

만약에 테스트를 위한 몇 가지 어썰션들이 있다면, 그것들 모두는 테스트가 PASS를 리턴 받기 위함을 만족해야만 한다.

테스트 실행프로그램은 모든 테스트 함수들의 정돈된 목록을 가질 것이다. 그것은 순서대로 실행될 것이다. 만약에 테스트가 실패하면 테스트 실행 스크립트는 정지하고 에러를 보고할 것이다. 여기에 테스트들을 생성할 코드가 있다.:
  
public function buildClassTests($class)
  {
    
    $methods = $class->xpath("method");
    $tests   = array();
    foreach($methods as $method)
      {
    $tests[] = build_Tests($method);
      }
    $filename  = $class["name"] . ".tests.php";
    $test_file =  join("",$tests);
    
    file_put_contents($filename, $test_file);
    
  }

  private function build_tests($method)
  {
    $tests      = $method->xpath("test");
    $test_name  = $method["name"];
    foreach($test as $test)
      {
    $test_name = $test["name"];
    
    $parameters = $test->xpath("cons_parameter");
    
    foreach($parameter as $param)
      {
        if($param["value"] == "DEFAULT")
          break;
        $cons_p[] = """.$param["value"] .".";
      }
    
    $parameters = $test->xpath("parameter");
    $p = array();
      
    foreach($parameter as $param)
    {
      if($param["value"] == "DEFAULT")
        break;
      $p[] = """ . $param["value"] .""";
    }
    $p = join(",",$p);
    $assert     = array();

    $exception        = "Exception";
    $exceptionReturn  = "FAIL";
    
    $assertions = $test->xpath("assertion");
    /* 현재 코드는 하나의 어썰션만 지원하지만, 때론 하나 이상이 더 유용할것이다*/
    foreach($assertions  as $a)
      {
        switch($a["type"])
          {
          case "return":
        $assert[] = sprintf("%11s%20s%4s%20s","","$result",$a["cond"],$a["value"]);
        break;
          case "exception":
        $exception = $a["exception"];
        $exceptionReturn = "PASS";
          }
      }
    
    if($exception != "Exception")
      $exep           = "           catch (Exception $e) \n".
        "           {\n".
        "               return FAIL;".
        "         \n}\n";
    else
      $exep = "";

    if($assert)
      {
        $assert_block = "           if(\n". join("&&\n",$assert) . "             )\n";
        $assert_block = "              return PASS\n";
        $assert_block = "           else\n";
        $assert_block = "              return FAIL\n";
        
      }
    

      $code = <<$name($p);
$assert
      }
      catch ($exception \$e)
      {
        return $exceptionReturn
      }
      $excp
      
CODE;
      $tests[] = $code;
    }
      return join("\n\n\n",$tests);
    }
이것은 간단한 테스트 케이스이다. 만약에 메소드가 true를 리턴하면 통과하고, 
만약에 false나 예외를 리턴하면 실패 하게 된다.
    function test_test4()
    {
      try
      {
           
         $class  = new testClass();
         $result = $class->execute("12345");
             if($result == true)
               return PASS;
             else 
               return FAIL;
      }
      catch (Exception $e)
      {
        return FAIL;
      }
여기에 코드 실행프로그램(code runner)을 기재 하기 위해(for listing) 생성된 코드가 있다:
";
  else
  {
    echo "$i Test $test FAIL
"; $fail = true; break; } } if($fail) echo "SOME TESTS FAILED"; else echo "ALL TESTS PASSED"; ?>

역자 유일호님은 현재 어느 중소기업의 소프트웨어 엔지니어로 근무하고 있으며, 잡다한 스킬 덕에 여러 가지 개발관련 일을 맡아서 하고 있습니다. 최근에는 리버스 엔지니어링(Reverse Engineering)에 관심이 많습니다.
TAG :
댓글 입력
자료실