격리 테스트
플러그인을 개발할 때 테스트하는 가장 좋은 방법 은 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로 남겨 두는 사용자 지정 데이터베이스 테스트 용 제품군을 작성할 수 있습니다 .
모의 객체를 사용하면 데이터베이스를 처리하지 않고도 대부분의 클래스를 올바르게 테스트 할 수 있도록 가능한 한 많은 데이터베이스 작업을 추상화하는 클래스 만 작성해야합니다.
세 번째로 쉽게 테스트 할 수있는 코드 작성은 더 나은 코드 작성을 의미합니다.
phpunit
테스트 실패 또는 통과를 볼 수 있습니까? 설치 했습니까bin/install-wp-tests.sh
?