ทดสอบแยก
เมื่อพัฒนาปลั๊กอินวิธีที่ดีที่สุดในการทดสอบคือไม่ต้องโหลดสภาพแวดล้อม WordPress
ถ้าคุณเขียนโค้ดที่สามารถทดสอบได้อย่างง่ายดายโดยไม่ WordPress, รหัสของคุณจะกลายเป็นดีกว่า
ทุกองค์ประกอบที่ถูกทดสอบหน่วยควรได้รับการทดสอบแยก : เมื่อคุณทดสอบคลาสคุณจะต้องทดสอบเฉพาะคลาสนั้นโดยสมมติว่าโค้ดอื่น ๆ นั้นทำงานได้อย่างสมบูรณ์
นี่คือเหตุผลที่การทดสอบหน่วยเรียกว่า "หน่วย"
ในฐานะที่เป็นประโยชน์เพิ่มเติมหากไม่มีการโหลดคอร์การทดสอบของคุณจะทำงานได้เร็วขึ้นมาก
หลีกเลี่ยง hooks ในตัวสร้าง
เคล็ดลับที่ฉันสามารถให้คุณได้คือหลีกเลี่ยงการใส่ตะขอในตัวสร้าง นั่นเป็นหนึ่งในสิ่งที่จะทำให้รหัสของคุณทดสอบได้โดยแยกออกจากกัน
ลองดูรหัสทดสอบใน OP:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation() {
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
และสมมติว่าการทดสอบนี้ล้มเหลว ใครคือผู้ร้าย ?
- เบ็ดไม่ถูกเพิ่มเลยหรือไม่ถูกต้อง?
- วิธีการที่ลงทะเบียนประเภทโพสต์ไม่ได้เรียกว่าทั้งหมดหรือมีข้อโต้แย้งที่ไม่ถูกต้อง?
- มีข้อผิดพลาดใน WordPress?
จะปรับปรุงได้อย่างไร
สมมติว่ารหัสเรียนของคุณคือ:
class RegisterCustomPostType {
function init() {
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type() {
register_post_type( 'foo' );
}
}
(หมายเหตุ: ฉันจะอ้างถึงรุ่นของชั้นเรียนนี้สำหรับคำตอบที่เหลือ)
add_action
วิธีที่ผมเขียนชั้นนี้ช่วยให้คุณสร้างอินสแตนซ์ของชั้นโดยไม่ต้องโทร
ในชั้นเรียนด้านบนมี 2 สิ่งที่ต้องทำการทดสอบ:
- วิธีการโทร
init
จริงadd_action
ผ่านไปยังอาร์กิวเมนต์ที่เหมาะสม
- วิธีการ
register_post_type
จริงเรียกregister_post_type
ฟังก์ชั่น
ฉันไม่ได้บอกว่าคุณต้องตรวจสอบว่ามีประเภทโพสต์หรือไม่: ถ้าคุณเพิ่มการกระทำที่เหมาะสมและถ้าคุณเรียกregister_post_type
ใช้ประเภทโพสต์ที่กำหนดเองจะต้องมีอยู่: หากไม่มีอยู่นั้นเป็นปัญหาของ WordPress
โปรดจำไว้ว่าเมื่อคุณทดสอบปลั๊กอินของคุณคุณมีการทดสอบของคุณรหัสไม่รหัส WordPress ในการทดสอบของคุณคุณต้องสมมติว่า WordPress (เช่นเดียวกับห้องสมุดภายนอกอื่น ๆ ที่คุณใช้) นั้นทำงานได้ดี นั่นคือความหมายของการทดสอบหน่วย
แต่ ... ในทางปฏิบัติ
หาก WordPress ไม่โหลดถ้าคุณพยายามเรียกวิธีการเรียนด้านบนคุณจะได้รับข้อผิดพลาดร้ายแรงดังนั้นคุณต้องจำลองฟังก์ชั่น
วิธีการ "ด้วยตนเอง"
ตรวจสอบให้แน่ใจว่าคุณสามารถเขียนไลบรารีที่เยาะเย้ยหรือจำลอง "ด้วยตนเอง" ได้ทุกวิธี มันเป็นไปได้. ฉันจะบอกวิธีการทำ แต่แล้วฉันจะแสดงวิธีที่ง่ายขึ้น
หาก WordPress ไม่โหลดในขณะที่การทดสอบกำลังทำงานอยู่ก็หมายความว่าคุณสามารถ redefine หน้าที่ของตนเช่นหรือadd_action
register_post_type
สมมติว่าคุณมีไฟล์โหลดจากไฟล์ bootstrap ที่คุณมี:
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!
ใช่ถ้าคุณต้องเยาะเย้ยฟังก์ชั่น WordPress ทั้งหมดด้วยตนเองมันเป็นเรื่องที่เจ็บปวดจริงๆ คำแนะนำทั่วไปที่ฉันสามารถให้ได้คือใช้ฟังก์ชั่น WP ให้น้อยที่สุดเท่าที่จะทำได้: คุณไม่จำเป็นต้องเขียน WordPress ใหม่แต่ฟังก์ชั่น WP แบบนามธรรมที่คุณใช้ในคลาสที่กำหนดเองเพื่อให้สามารถจำลองและทดสอบได้อย่างง่ายดาย
เช่นเกี่ยวกับตัวอย่างข้างต้นคุณสามารถเขียนคลาสที่ลงทะเบียนประเภทการโพสต์เรียกregister_post_type
ใช้ 'init' พร้อมอาร์กิวเมนต์ที่กำหนด ด้วยนามธรรมนี้คุณยังต้องทดสอบคลาสนั้น แต่ในที่อื่น ๆ ของรหัสของคุณที่ลงทะเบียนประเภทโพสต์คุณสามารถใช้คลาสนั้นเยาะเย้ยในการทดสอบ (ดังนั้นสมมติว่ามันใช้งานได้)
สิ่งที่น่ากลัวคือถ้าคุณเขียนชั้นเรียนว่าการลงทะเบียน CPT บทคัดย่อคุณสามารถสร้างพื้นที่เก็บข้อมูลที่แยกต่างหากสำหรับมันและขอบคุณที่เครื่องมือที่ทันสมัยเช่นนักแต่งเพลงที่ฝังไว้ในทุกโครงการที่คุณต้องการ: การทดสอบครั้งเดียวใช้งานได้ทุกที่ และหากคุณเคยพบข้อผิดพลาดในนั้นคุณสามารถแก้ไขได้ในที่เดียวและด้วยcomposer update
โครงการทั้งหมดที่ใช้งานง่ายได้รับการแก้ไขด้วย
สำหรับครั้งที่สอง: การเขียนโค้ดที่สามารถทดสอบแยกได้หมายถึงการเขียนโค้ดที่ดีขึ้น
แต่ไม่ช้าก็เร็วฉันจำเป็นต้องใช้ฟังก์ชั่น WP บางแห่ง ...
แน่นอน. คุณไม่ควรทำตัวขนานกับแกนมันไม่สมเหตุสมผล คุณสามารถเขียนคลาสที่ล้อมฟังก์ชัน WP แต่คลาสเหล่านั้นจำเป็นต้องทดสอบด้วย วิธีการ "ด้วยตนเอง" ที่อธิบายไว้ข้างต้นอาจถูกใช้สำหรับงานง่าย ๆ แต่เมื่อคลาสมีฟังก์ชัน WP จำนวนมากอาจเป็นความเจ็บปวด
โชคดีที่นั่นมีคนดีที่เขียนสิ่งที่ดี 10upซึ่งเป็นหนึ่งในเอเจนซี่ WP ที่ใหญ่ที่สุดยังคงรักษาไลบรารี่ที่ยอดเยี่ยมสำหรับผู้ที่ต้องการทดสอบปลั๊กอินอย่างถูกวิธี WP_Mock
มันเป็น
จะช่วยให้คุณฟังก์ชั่น WP จำลองตะขอ สมมติว่าคุณโหลดในการทดสอบของคุณ (ดู repo readme) การทดสอบเดียวกันที่ฉันเขียนข้างต้นกลายเป็น:
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 มีระบบฝังตัวเพื่อจำลองวัตถุ แต่ที่นี่ฉันต้องการแนะนำการเยาะเย้ยให้กับคุณ มันเป็นห้องสมุดที่ทรงพลังมากและใช้งานง่ายมาก ยิ่งกว่านั้นมันเป็นสิ่งที่ต้องพึ่งพาWP_Mock
ดังนั้นถ้าคุณมีคุณก็จะมีการเยาะเย้ยเช่นกัน
แต่เกี่ยวกับWP_UnitTestCase
อะไร
ชุดทดสอบ WordPress ถูกสร้างขึ้นเพื่อทดสอบหลักของ WordPress และหากคุณต้องการมีส่วนร่วมในแกนหลักนั้นเป็นสิ่งสำคัญ แต่การใช้มันสำหรับปลั๊กอินจะทำให้คุณไม่ได้แยก
ทำให้สายตาของคุณเหนือโลก WP: มีเฟรมเวิร์คสเตชั่นของ PHP และ CMS อยู่มากมายและไม่มีใครแนะนำให้ทดสอบปลั๊กอิน / โมดูล / ส่วนขยาย (หรือสิ่งที่พวกเขาเรียกว่า) โดยใช้โค้ดเฟรมเวิร์ก
หากคุณพลาดโรงงานคุณลักษณะที่มีประโยชน์ของชุดคุณต้องรู้ว่ามีสิ่งที่น่ากลัวอยู่ที่นั่น
Gotchas และข้อเสีย
มีกรณีคือเมื่อขั้นตอนการทำงานที่ผมแนะนำที่นี่ขาด: การทดสอบฐานข้อมูลที่กำหนดเอง
ในความเป็นจริงถ้าคุณใช้ตาราง WordPress มาตรฐานและฟังก์ชั่นในการเขียนมี (ที่ระดับต่ำสุด$wpdb
วิธีการ) คุณไม่จำเป็นต้องจริงเขียนข้อมูลหรือการทดสอบหากข้อมูลเป็นจริงในฐานข้อมูลเพียงให้แน่ใจว่าวิธีการที่เหมาะสมจะเรียกว่ามีข้อโต้แย้งที่เหมาะสม
อย่างไรก็ตามคุณสามารถเขียนปลั๊กอินด้วยตารางและฟังก์ชั่นที่กำหนดเองที่สร้างคิวรีเพื่อเขียนและทดสอบว่าคิวรี่เหล่านั้นทำงานได้หรือไม่
ในกรณีผู้ WordPress ชุดทดสอบที่สามารถช่วยให้คุณมากและโหลด WordPress dbDelta
อาจมีความจำเป็นในบางกรณีที่จะเรียกใช้ฟังก์ชั่นเช่น
(ไม่จำเป็นต้องบอกว่าใช้ db อื่นสำหรับการทดสอบใช่ไหม?)
โชคดีที่ PHPUnit ช่วยให้คุณสามารถจัดระเบียบการทดสอบของคุณได้ใน "สวีท" ที่สามารถทำงานแยกกันเพื่อให้คุณสามารถเขียนชุดสำหรับการทดสอบฐานข้อมูลที่กำหนดเองที่คุณโหลดสภาพแวดล้อม WordPress (หรือส่วนหนึ่งของมัน) ออกจากส่วนที่เหลือทั้งหมดของการทดสอบของคุณWordPress ฟรี
ให้แน่ใจว่าได้เขียนคลาสที่เป็นนามธรรมการดำเนินการฐานข้อมูลมากที่สุดเท่าที่เป็นไปได้ในลักษณะที่คลาสปลั๊กอินอื่น ๆ ใช้ประโยชน์จากคลาสนั้นเพื่อให้การใช้ mocks คุณสามารถทดสอบคลาสส่วนใหญ่โดยไม่ต้องจัดการกับฐานข้อมูล
สำหรับครั้งที่สามการเขียนโค้ดที่สามารถทดสอบแยกได้ง่ายหมายถึงการเขียนโค้ดที่ดีกว่า
phpunit
คุณจะเห็นการทดสอบที่ล้มเหลวหรือผ่านการทดสอบแล้วหรือไม่ คุณไม่ติดตั้งbin/install-wp-tests.sh
?