콜백 테스트


34

TDD를 사용하여 플러그인을 개발 중이며 완전히 테스트하지 못한 것은 ... 훅입니다.

나는 괜찮습니다. 후크 콜백을 테스트 할 수는 있지만 후크가 실제로 트리거되는지 (사용자 정의 후크와 WordPress 기본 후크) 어떻게 테스트 할 수 있습니까? 나는 조롱이 도움이 될 것이라고 생각하지만, 내가 잃어버린 것을 알아낼 수는 없습니다.

WP-CLI와 함께 테스트 스위트를 설치했습니다. 이 답변 에 따르면 , init후크는 트리거해야하지만 ... 그렇지 않습니다. 또한 코드는 WordPress 내에서 작동합니다.

내 이해에서 부트 스트랩은 마지막에로드되므로 init을 트리거하지 않는 것이 합리적입니다. 따라서 남은 문제는 후크가 트리거되는지 어떻게 테스트 해야하는지입니다.

감사!

부트 스트랩 파일은 다음과 같습니다.

$_tests_dir = getenv('WP_TESTS_DIR');
if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';

require_once $_tests_dir . '/includes/functions.php';

function _manually_load_plugin() {
  require dirname( __FILE__ ) . '/../includes/RegisterCustomPostType.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );

require $_tests_dir . '/includes/bootstrap.php';

테스트 된 파일은 다음과 같습니다.

class RegisterCustomPostType {
  function __construct()
  {
    add_action( 'init', array( $this, 'register_post_type' ) );
  }

  public function register_post_type()
  {
    register_post_type( 'foo' );
  }
}

그리고 테스트 자체 :

class CustomPostTypes extends WP_UnitTestCase {
  function test_custom_post_type_creation()
  {
    $this->assertTrue( post_type_exists( 'foo' ) );
  }
}

감사!


를 실행중인 경우 phpunit테스트 실패 또는 통과를 볼 수 있습니까? 설치 했습니까 bin/install-wp-tests.sh?
Sven

문제의 일부 RegisterCustomPostType::__construct()는 플러그인이 테스트를 위해로드 될 때 호출되지 않을 것이라고 생각합니다 . 버그 # 29827의 영향을받는 것도 가능합니다 . WP의 단위 테스트 스위트 버전을 업데이트하십시오.
JD

@Sven : 예, 테스트가 실패했습니다. 설치 bin/install-wp-tests.sh(wp-cli를 사용 했으므로) @JD : RegisterCustomPostType :: __ construct 호출됩니다 (단지 die()명령문을 추가 하고 phpunit이 중지됨)
Ionut Staicu

단위 테스트 측면 (내 장점이 아님)에 대해서는 확신이 없지만 문자 그대로의 관점에서 did_action()작업이 시작되었는지 확인할 수 있습니다 .
Rarst

@Rarst : 제안 해 주셔서 감사하지만 여전히 작동하지 않습니다. 어떤 이유로 타이밍이 잘못되었다고 생각합니다 (테스트는 init후크 전에 실행됩니다 ).
Ionut Staicu

답변:


72

격리 테스트

플러그인을 개발할 때 테스트하는 가장 좋은 방법 은 WordPress 환경 로드 하지 않는 것입니다.

WordPress없이 쉽게 테스트 할 수있는 코드를 작성하면 코드가 향상 됩니다.

단위 테스트되는 모든 구성 요소는 별도로 테스트해야합니다 . 클래스를 테스트 할 때 다른 모든 코드가 완벽하게 작동한다고 가정하면 해당 클래스 만 테스트하면됩니다.

아이솔레이터

이것이 단위 테스트가 "단위"라고하는 이유입니다.

추가 이점으로, 코어를로드하지 않고도 테스트가 훨씬 빠르게 실행됩니다.

생성자에서 후크 방지

내가 줄 수있는 팁은 생성자에 후크를 넣지 않는 것입니다. 그것은 코드를 독립적으로 테스트 할 수있게 해주는 것 중 하나입니다.

OP에서 테스트 코드를 보자.

class CustomPostTypes extends WP_UnitTestCase {
  function test_custom_post_type_creation() {
    $this->assertTrue( post_type_exists( 'foo' ) );
  }
}

그리고이 테스트 가 실패 했다고 가정 해 봅시다 . 범인 은 누구 입니까?

  • 후크가 전혀 추가되지 않았거나 제대로 설치되지 않았습니까?
  • 게시 유형을 등록하는 메소드가 전혀 호출되지 않았거나 잘못된 인수로?
  • 워드 프레스에 버그가 있습니까?

어떻게 개선 할 수 있습니까?

클래스 코드가 다음과 같다고 가정 해 봅시다.

class RegisterCustomPostType {

  function init() {
    add_action( 'init', array( $this, 'register_post_type' ) );
  }

  public function register_post_type() {
    register_post_type( 'foo' );
  }
}

(참고 : 나머지 답변에 대해서는이 버전의 클래스를 참조 할 것입니다)

이 클래스를 작성한 방법을 사용하면 호출하지 않고 클래스의 인스턴스를 만들 수 있습니다 add_action.

위의 클래스에는 테스트해야 할 두 가지가 있습니다.

  • 이 메소드는 init 실제로add_action 적절한 인수를 전달하여 호출합니다.
  • 이 메소드는 register_post_type 실제로register_post_type 함수를 호출 합니다.

게시물 유형이 있는지 확인해야한다고 말하지 않았습니다 : 적절한 조치를 추가하고을 호출 register_post_type하면 사용자 정의 게시물 유형 존재 해야 합니다. 존재하지 않는 경우 WordPress 문제입니다.

플러그인을 테스트 때는 워드 프레스 코드가 아닌 코드 를 테스트 해야 합니다. 테스트에서 사용하는 다른 외부 라이브러리와 마찬가지로 WordPress가 잘 작동한다고 가정해야합니다. 이것이 단위 테스트 의 의미입니다 .

하지만 ... 실제로?

WordPress가로드되지 않은 경우 위의 클래스 메소드를 호출하려고하면 치명적인 오류가 발생하므로 함수를 조롱해야합니다.

"수동"방법

물론 모의 라이브러리를 작성하거나 모든 방법을 "수동으로"모의 할 수 있습니다. 있을 수있다. 그 방법을 알려 드리지만 더 쉬운 방법을 보여 드리겠습니다.

테스트가 실행되는 동안 워드 프레스가로드되지 않으면 기능을 다시 정의 할 수 있습니다 (예 : add_action또는) register_post_type.

부트 스트랩 파일에서로드 된 파일이 있다고 가정 해 봅시다.

function add_action() {
  global $counter;
  if ( ! isset($counter['add_action']) ) {
    $counter['add_action'] = array();
  }
  $counter['add_action'][] = func_get_args();
}

function register_post_type() {
  global $counter;
  if ( ! isset($counter['register_post_type']) ) {
    $counter['register_post_type'] = array();
  }
  $counter['register_post_type'][] = func_get_args();
}

호출 될 때마다 전역 배열에 요소를 추가하기 위해 함수를 다시 작성했습니다.

이제 테스트 PHPUnit_Framework_TestCase를 쉽게 구성 할 수있는 기본 테스트 케이스 클래스 확장을 작성해야합니다 (아직없는 경우) .

다음과 같이 될 수 있습니다.

class Custom_TestCase extends \PHPUnit_Framework_TestCase {

    public function setUp() {
        $GLOBALS['counter'] = array();
    }

}

이러한 방식으로 모든 테스트 전에 글로벌 카운터가 재설정됩니다.

그리고 이제 테스트 코드 ( 위에 게시 한 다시 작성된 클래스를 참조하십시오 ) :

class CustomPostTypes extends Custom_TestCase {

  function test_init() {
     global $counter;
     $r = new RegisterCustomPostType;
     $r->init();
     $this->assertSame(
       $counter['add_action'][0],
       array( 'init', array( $r, 'register_post_type' ) )
     );
  }

  function test_register_post_type() {
     global $counter;
     $r = new RegisterCustomPostType;
     $r->register_post_type();
     $this->assertSame( $counter['register_post_type'][0], array( 'foo' ) );
  }

}

다음 사항에 유의하십시오.

  • 두 가지 방법을 별도로 호출 할 수 있었고 WordPress가 전혀로드되지 않았습니다. 이 방법으로 한 번의 테스트에 실패 하면 범인이 누구 인지 정확히 있습니다.
  • 내가 말했듯이, 여기에서는 클래스가 예상 인수로 WP 함수를 호출하는지 테스트합니다. CPT가 실제로 존재하는지 테스트 할 필요가 없습니다. CPT의 존재를 테스트하는 경우 플러그인 동작이 아닌 WordPress 동작을 테스트하는 중입니다 ...

좋은 .. 그러나 그것은 PITA이다!

네, 모든 워드 프레스 기능을 수동으로 조롱해야한다면 정말 고통 스럽습니다. 당신이 필요가 없습니다 : 내가 줄 수있는 몇 가지 일반적인 조언은 가능한 몇 WP 기능으로 사용하는 것입니다 다시 워드 프레스를하지만, 추상적 인 WP 기능 당신은 그들이 조롱하고 쉽게 테스트 할 수 그래서, 사용자 정의 클래스에서 사용합니다.

예를 들어 위의 예와 관련 register_post_type하여 주어진 인수로 'init'를 호출 하여 게시물 유형을 등록하는 클래스를 작성할 수 있습니다 . 이 추상화를 사용하면 해당 클래스를 테스트해야하지만 게시물 유형을 등록하는 다른 코드 위치에서 해당 클래스를 사용하여 테스트에서 조롱 할 수 있습니다 (작동한다고 가정).

가장 좋은 점은 CPT 등록을 추상화하는 클래스를 작성하면 별도의 리포지토리를 만들 수 있으며 Composer 와 같은 최신 도구 덕분에 필요한 모든 프로젝트에 포함시킬 수 있습니다 . 한 번 테스트하고 어디서나 사용하십시오 . 그리고 버그를 발견하면 한곳에서 버그를 고칠 수 있으며 사용 된 composer update모든 프로젝트도 간단하게 수정할 수 있습니다.

두 번째로 : 격리 가능한 테스트 가능한 코드 작성은 더 나은 코드를 작성하는 것을 의미합니다.

그러나 조만간 WP 기능을 어딘가에 사용해야합니다 ...

당연하지. 코어 와 평행하게 행동해서는 안됩니다 . WP 함수를 래핑하는 클래스를 작성할 수 있지만 해당 클래스도 테스트해야합니다. 위에서 설명한 "수동"방법은 매우 간단한 작업에 사용될 수 있지만 클래스에 많은 WP 함수가 포함되어 있으면 고통 스러울 수 있습니다.

다행히저기서 좋은 것을 쓰는 좋은 사람들이 있습니다. 가장 큰 WP 대행사 중 하나 인 10up 은 올바른 방식으로 플러그인을 테스트하려는 사람들을 위해 매우 훌륭한 라이브러리를 유지 관리합니다. 입니다 WP_Mock.

그것은 당신이 후크를 WP 기능조롱 할 수 있습니다 . 테스트에로드했다고 가정하면 (리포 읽기 참조) 위에서 작성한 것과 동일한 테스트가됩니다.

class CustomPostTypes extends Custom_TestCase {

  function test_init() {
     $r = new RegisterCustomPostType;
     // tests that the action was added with given arguments
     \WP_Mock::expectActionAdded( 'init', array( $r, 'register_post_type' ) );
     $r->init();
  }

  function test_register_post_type() {
     // tests that the function was called with given arguments and run once
     \WP_Mock::wpFunction( 'register_post_type', array(
        'times' => 1,
        'args' => array( 'foo' ),
     ) );
     $r = new RegisterCustomPostType;
     $r->register_post_type();
  }

}

간단하지 않습니까? 이 답변은에 대한 자습서가 아니므로 WP_Mock자세한 내용은 repo readme를 읽으십시오. 그러나 위의 예는 꽤 분명해야합니다.

또한 조롱 add_action하거나 register_post_type직접 작성 하거나 전역 변수를 유지할 필요가 없습니다 .

그리고 WP 수업?

WP에는 일부 클래스가 있으며 테스트를 실행할 때 WordPress가로드되지 않으면 해당 클래스를 조롱해야합니다.

그것은 모의 함수보다 훨씬 쉽습니다. PHPUnit에는 객체를 조롱하는 임베디드 시스템이 있지만 여기서는 Mockery 를 제안하고 싶습니다 . 매우 강력한 라이브러리이며 사용하기 매우 쉽습니다. 또한의 의존성 WP_Mock이므로 Mockery도 있습니다.

그러나 어떻 WP_UnitTestCase습니까?

워드 프레스 테스트 스위트는 워드 프레스 코어 를 테스트하기 위해 만들어졌으며 코어 에 기여하고 싶다면 핵심이지만 플러그인에 사용하면 격리되지 않은 테스트 만 할 수 있습니다.

WP 세계를 주시하십시오. 현대 PHP 프레임 워크와 CMS가 많이 있으며 프레임 워크 코드를 사용하여 플러그인 / 모듈 / 확장 (또는 호출 된 것)을 테스트 할 것을 제안하지 않습니다.

스위트의 유용한 기능인 팩토리를 놓치면 멋진 것들이 있다는 것을 알아야합니다 .

단점과 단점

여기에 제안한 워크 플로에 사용자 지정 데이터베이스 테스트 가없는 경우가 있습니다 .

당신은 (가장 낮은 레벨에서이 작성하는 표준 워드 프레스 테이블과 기능을 사용하는 경우 사실, $wpdb방법) 당신은 필요가 없습니다 실제로 데이터 인 경우 쓰기 데이터 또는 테스트 실제로 데이터베이스에서 바로 확인 적절한 방법이 적절한 인수와 함께 호출되는 수.

그러나 사용자 정의 테이블 및 함수를 사용하여 플러그인을 작성하여 작성하여 쿼리를 작성하고 해당 쿼리가 작동하는지 테스트 할 수 있습니다.

이 경우 WordPress 테스트 스위트는 많은 도움을 줄 수 있으며, WordPress로드는 경우에 따라와 같은 기능을 실행하는 데 필요할 수 있습니다 dbDelta.

(테스트를 위해 다른 db를 사용할 필요가 없습니까?)

운 좋게도 PHPUnit을 사용하면 별도로 실행할 수있는 "스위트"로 테스트를 구성 할 수 있으므로 WordPress 환경 (또는 그 일부)을로드하는 나머지 사용자 정의 테스트를 WordPress-free로 남겨 두는 사용자 지정 데이터베이스 테스트 용 제품군을 작성할 수 있습니다 .

모의 객체를 사용하면 데이터베이스를 처리하지 않고도 대부분의 클래스를 올바르게 테스트 할 수 있도록 가능한 한 많은 데이터베이스 작업을 추상화하는 클래스 만 작성해야합니다.

세 번째로 쉽게 테스트 할 수있는 코드 작성은 더 나은 코드 작성을 의미합니다.


5
이런 쓰레기, 유용한 정보가 많이 있습니다! 고맙습니다! 어떻게 든 단위 테스트의 모든 요점을 놓쳤습니다 (지금까지는 Code Dojo 내부에서만 PHP 테스트를 연습하고있었습니다). 나는 또한 오늘 일찍 wp_mock에 대해 발견했지만 어떤 이유로 든 무시할 수 있습니다. 나를 화나게 한 것은 모든 테스트가 아무리 작더라도 실행하는 데 최소 2 초가 걸린다는 것입니다 (WP env를 먼저로드하고 두 번째 테스트를 실행하십시오). 내 눈을 뜨고 다시 한번 감사합니다!
Ionut Staicu

4
덕분에 그로드되지 워드 프레스는 당신의 시험하게 언급에 깜빡 @IonutStaicu 훨씬 빠르게
gmazzap

6
또한 WP Core 유닛 테스트 프레임 워크는 INTEGRATION 테스트를 실행하기위한 놀라운 도구이며, WP 자체와 잘 통합되는지 확인하기위한 자동화 된 테스트 (예 : 우발적 인 기능 이름 충돌 등)가 아니라는 점을 지적 할 가치가 있습니다.
John P Bloch

1
좋은 지적을 위해 @JohnPBloch +1. 네임 스페이스를 사용하는 것이 WordPress에서 함수 이름 충돌을 피하기에 충분하더라도 모든 것이 전역 적입니다 :) 그러나 통합 / 기능 테스트는 중요합니다. 나는 현재 Behat + Mink와 놀고 있지만 여전히 연습하고 있습니다.
gmazzap

1
WordPress의 UnitTest 포리스트를 통해 "헬리콥터를 타고"주셔서 감사합니다-나는 아직도 그 서사시 사진을보고 웃고 있습니다 ;-)
birgire
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.