พื้นที่เก็บข้อมูลสำหรับเวิร์กช็อปของฉันที่ WordCamp Catania 2019
ไม่จำเป็น แต่คุณอาจต้องติดตั้งนักเทียบท่า:
sudo apt-get install docker-ce docker-ce-cli containerd.io
ฉันถือว่าคุณติดตั้ง Composer แล้ว มาติดตั้ง PHPUnit ก่อน:
composer require --dev phpunit/phpunit ^8.3
โปรดตรวจสอบข้อกำหนดด้วย!
PHPUnit 8.3 ต้องการอย่างน้อย PHP 7.2! อย่างไรก็ตาม - การสนับสนุนด้านความปลอดภัยสำหรับ PHP 7.1 จะสิ้นสุดในวันที่ 1 ธันวาคม 2019
คำแนะนำ : คุณไม่ได้ติดตั้ง Composer ใช่หรือไม่ ลองสิ่งนี้!
docker run --rm -it -v $PWD:/app -u $(id -u):$(id -g) composer install
มีเฟรมเวิร์กที่ถูกต้องอย่างน้อยสองเฟรมซึ่งมีประโยชน์เมื่อคุณวางแผนทดสอบส่วนขยาย WordPress:
มาลอง Brain Monkey :
composer require --dev brain/monkey:2.*`
สิ่งนี้จะติดตั้ง Mockery และ Patchwork โดยอัตโนมัติ เพียงดำเนินการ composer install
และคุณก็พร้อมแล้ว
สร้างไดเร็กทอรีที่จะมอบบ้านให้กับคลาสทดสอบขนาดเล็กชื่อ WcctaTest.php :
mkdir -p tests/wccta
ยอดเยี่ยม! ตอนนี้เรามาสร้างไฟล์คอนฟิกูเรชัน phpunit.xml ในไดเร็กทอรีราก
คุณยังสามารถตัดสินใจรันการทดสอบด้วยพารามิเตอร์การกำหนดค่าจากบรรทัดคำสั่งได้ ดูส่วนถัดไป (คำใบ้: 'สคริปต์')!
ยอดเยี่ยม! เพิ่มบางส่วนลงในไฟล์ composer.json :
composer test
ได้แล้ว มาสร้างไดเร็กทอรีที่จะให้ซอร์สโค้ดของเราเป็นบ้าน นี่คือสถานที่ที่คุณจะจัดชั้นเรียนระดับเฟิร์สคลาสที่คุณจะทดสอบเร็วๆ นี้
mkdir -p src/wccta && touch src/wccta/Plugin.php
rm -f tests/wccta/WcctaTest.php && touch tests/wccta/PluginTest.php
touch wordpress-plugins-phpunit.php
เราต้องการทดสอบวิธีการบางอย่างของคลาส Plugin
ลองนึกภาพวิธีการที่เรียกว่า is_loaded
ซึ่งคืนค่า true
เมื่อประสบความสำเร็จ เมื่อคุณพร้อมแล้ว ให้ดำเนินการ:
composer test
คำแนะนำ : ระบบหรือเวอร์ชัน PHP ของคุณไม่อัปเดตใช่หรือไม่ คุณสามารถข้ามขั้นตอนนี้ไปได้ แต่มาลองอะไรใหม่ๆ [ไม่มาก] กันดีกว่า!
docker run -it --rm -v $PWD:/app -w /app php:7.3-alpine php ./vendor/bin/phpunit
คุณอาจจินตนาการได้ว่าปลั๊กอินบางตัวจะมีคลาสจำนวนมาก และคุณสามารถลืมทดสอบฟังก์ชันทั้งหมดที่จำเป็นต้องทดสอบได้อย่างง่ายดาย
เอาล่ะ เรามาพูดถึงเรื่อง Coverage กันดีกว่า!
เพียงเพิ่มคำสั่งที่กำหนดเองในส่วน scripts-section ใน composer.json ของคุณ :
"coverage": "./vendor/bin/phpunit --coverage-html ./reports/php/coverage"
และตัวกรองไปยัง phpunit.xml ของคุณ:
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>./src</directory>
</whitelist>
</filter>
ตอนนี้เพียงดำเนินการ composer coverage
! สิ่งนี้จะสร้างไดเร็กทอรี ./reports/php/coverage
พร้อมกับไฟล์ html บางไฟล์ ไม่ใช่ในคอมพิวเตอร์ทุกเครื่อง บางคนจะยังคงได้รับข้อความแสดงข้อผิดพลาดเช่น:
Error: No code coverage driver is available
มาแก้ไขมันใน docker-image ของเรา ฉันเตรียม Dockerfile เพื่อให้คุณสามารถดำเนินการได้:
docker build -t coverage .
และหลังจากกระบวนการสร้างเสร็จสิ้น:
docker run -it --rm -v $PWD:/app -w /app coverage:latest php ./vendor/bin/phpunit --coverage-html ./reports/php/coverage
ตอนนี้คุณรู้แล้วว่ากังฟู! กรุณาเปิดไฟล์ ./reports/php/coverage/index.html ในเบราว์เซอร์ของคุณ!
มาเชื่อมโยง Plugin
-class ของเราเข้ากับปลั๊กอินกัน ก่อนที่เราจะเข้าสู่การทดสอบจริงๆ ฉันจะแสดงให้คุณเห็นถึงวิธีการประกาศโค้ดบางส่วนของคุณว่าไม่ควรทดสอบ
@codeCoverageIgnore
นี่เป็นหนึ่งในคำอธิบายประกอบที่สำคัญที่มีอยู่ เราจะกลับมาที่เรื่องนี้ในภายหลัง แต่ก่อนอื่น:
รัน unittests ด้วยรายงานความครอบคลุมอีกครั้ง!
คุณอาจสังเกตเห็นคอลัมน์ CRAP
ในรายงานความครอบคลุม CRAP เป็นตัวย่อสำหรับ การเปลี่ยนแปลงความเสี่ยงต่อต้านรูปแบบ มันบ่งชี้ว่าการเปลี่ยนแปลงโค้ดในคลาสหรือเมธอดนั้นมีความเสี่ยงเพียงใด คุณสามารถลดความเสี่ยง (และดัชนีด้วย) ด้วยโค้ดที่ซับซ้อนน้อยกว่า และ ครอบคลุมการทดสอบทั้งหมด
มาเริ่มทดสอบอะไรบางอย่างกัน แต่อะไรล่ะ? ยังไม่มีการเขียนฟังก์ชันเพิ่มเติมที่ต้องมีการทดสอบ
TDD (Test Driven Development) เข้ามาในเกมแล้ว
แม้ว่าคุณจะตัดสินใจที่จะ ไม่ ใช้เทคนิคนี้ แต่อย่างน้อยคุณก็ควรรู้ว่าเรากำลังพูดถึงอะไร
ขั้นแรกเรามาสร้าง Test CarTest
ที่ควรทดสอบว่าเมธอด get_price
ส่งกลับสตริง '€ 14.500'
หรือไม่ จากนั้นสร้าง Class Car
และเขียนเมธอด get_price
ที่ ตรงกับ การทดสอบ อย่าเริ่มต้นด้วยการนำไปปฏิบัติ
ณ จุดนี้ ฉันขอแนะนำรูปแบบการทดสอบ AAA (Arrange Act Assert) ซึ่งเป็นที่ยอมรับอย่างกว้างขวางใน TDD โดยจะอธิบายวิธีจัดเตรียมการทดสอบและคล้ายกับ GWT (Given When That) จาก BDD (Behavior-driven Development) มาก
คุณสามารถทดสอบชั้นเรียนของคุณได้หากชั้นเรียนมีข้อยกเว้นในบางเงื่อนไข ตอนนี้เรามาใช้งาน get_price
-method
เพียงสร้างคลาส Registry
ที่ตั้งค่าผสมเป็นรายการที่มีชื่อในอาร์เรย์ภายใน ใช้เมธอด set()
หรือเมธอดเวทย์มนตร์ __set()
สำหรับสิ่งนี้ ก่อนอื่นสมมติว่าเราสามารถส่งวัตถุ JSON ไปยังคลาส Car
ของเราได้ นี่จะทำให้ชั้นเรียนของเรามีคุณค่ามากขึ้นอีกเล็กน้อย
วิธีอื่น get
หรือ __get()
ควรตรวจสอบว่ามีรายการที่กำหนดอยู่หรือไม่ และส่งคืนเมื่อสำเร็จ หากไม่มีรายการดังกล่าว ให้ส่ง OutOfBoundsException
ตอนนี้ให้เขียน Constructor ที่จัดการอินพุต JSON และจัดเก็บอ็อบเจ็กต์ไว้ใน Member-var data
วิธี get_price
ควรใช้ราคาจาก data
var และดูแลเอาต์พุตที่จัดรูปแบบแล้ว
ตรวจสอบสาขา ขั้นตอนที่ 10 หากคุณมีเวลาที่ยากลำบากในการเขียนโค้ด! price
ผันแปรควรเป็นจำนวนเต็ม นี่อาจไม่มีปัญหาในขณะนี้ เนื่องจากคุณสามารถใช้ฟังก์ชัน PHP number_format()
เพื่อสร้างเอาต์พุตที่ถูกต้องได้ แต่ในการติดตั้ง WordPress คุณจะต้องมีการตั้งค่าภาษา เช่น it_IT
(ภาษาอิตาลี)
วิธีที่ถูกต้องในการจัดรูปแบบตัวเลขใน WordPress คือการใช้ฟังก์ชัน number_format_i18n()
ลองเปลี่ยนสิ่งนั้นและดูว่าเกิดอะไรขึ้น:
Error: Call to undefined function wcctanumber_format_i18n()
เราจะแก้ไขปัญหานี้ในอีกสักครู่ แต่ขอเตรียมตัวกันสักหน่อยก่อน Brain Monkey ใช้ setUp()
และ tearDown()
ที่จัดทำโดย PHPUnit คุณสามารถแทนที่วิธีการเหล่านั้นได้ มาสร้าง TestCase
แบบกำหนดเอง โดยตั้งชื่อว่า WcctaCase
ซึ่งเราสามารถขยายได้เนื่องจากเราจะทำเช่นนี้ในทุกคลาสการทดสอบ
ตอนนี้เรามารวมเนมสเปซสำหรับการทดสอบไว้ในส่วน autoload-dev:
"autoload-dev": {
"psr-4": {
"tests\wccta\": "tests/wccta"
}
},
สุดท้ายนี้ เรามาเปลี่ยนพาเรนต์ของคลาสการทดสอบของเรากัน
class CarTest extends WcctaTestCase { // ... }
เราพร้อมที่จะจำลอง WordPress ตัวแรกของเราแล้ว -ฟังก์ชั่นด้วย
Functionsexpect( $name_of_function )->andReturn( $value );
การเขียนแบบทดสอบโดยคาดหวังเพียงสิ่งเดียวดูเหมือนจะใช้ความพยายามมากเกินไป จะทำอย่างไรถ้าคุณต้องการทดสอบกับค่าที่ต่างกัน?
ผู้ให้บริการข้อมูลเพื่อช่วยเหลือ ฉันได้พูดคุยเกี่ยวกับคำอธิบายประกอบในขั้นตอนที่ 5 แล้ว คำอธิบายประกอบนี้มีประโยชน์มากเช่นกัน:
@dataprovider method_that_returns_data
ลองดูตัวอย่างของฉัน getData
ส่งคืนอาร์เรย์ของอาร์เรย์ แต่ละอาร์เรย์เหล่านี้มี 3 ค่า test_getPrice
-method ของเราไม่เพียงแต่ยอมรับ dataprovider ที่มีคำอธิบายประกอบเท่านั้น แต่ยังสามารถกำหนด input-vars เป็นพารามิเตอร์ได้อีกด้วย
คุณสามารถทดสอบชั้นเรียนของคุณได้หากชั้นเรียนมีข้อยกเว้นในบางเงื่อนไข
เพียงสร้างคลาส Registry
ที่ตั้งค่าผสมเป็นรายการที่มีชื่อในอาร์เรย์ภายใน ใช้เมธอด set()
หรือเมธอดเวทย์มนตร์ __set()
สำหรับสิ่งนี้
วิธีอื่น get
หรือ __get()
ควรตรวจสอบว่ามีรายการที่มีคีย์ที่กำหนดหรือไม่ และส่งคืนเมื่อสำเร็จ หากไม่มีรายการดังกล่าว ให้ส่ง OutOfBoundsException
ตรวจสอบสาขา ขั้นตอนที่ 10 หากคุณมีเวลาที่ยากลำบากในการเขียนโค้ด!
ขั้นตอนสุดท้ายนำเราไปสู่ โรงงาน โรงงานคืออะไร? บางครั้งคุณสร้างฟังก์ชันหรือเมธอดที่ซ่อนกระบวนการที่ซับซ้อนเพื่อสร้างวัตถุเฉพาะ และบางครั้งคุณต้องตัดสินใจว่าคุณต้องการสร้างออบเจ็กต์ประเภทใด
ในปลั๊กอิน WordPress ฉันชอบเพิ่ม hooks ในโรงงานให้กับวัตถุ มีปลั๊กอินที่เพิ่ม hooks ในตัวสร้างคลาส นี่ไม่ใช่เรื่องดี (โดยเฉพาะอย่างยิ่งเมื่อคุณยังคงทดสอบวิธีคลาสสิก - สร้างสภาพแวดล้อมที่สมบูรณ์ด้วย WordPress และทำงานอยู่)
มาสร้างคลาส Factory
ด้วยฟังก์ชันคงที่ชื่อ create
วิธีการนี้ควรส่งคืนวัตถุ Car
แต่ลองปรับโครงสร้างคอนสตรัคเตอร์ของ Car
อีกครั้ง เพื่อให้คาดว่าจะมีอ็อบเจ็กต์และไม่มีสตริง JSON เราจะทำสิ่งนี้ในวิธีการสร้างของ Factory
-class แทน
ทดสอบปลั๊กอินของคุณทันทีด้วย composer test
แล้วคุณจะเห็นข้อผิดพลาดบางอย่าง:
TypeError: Argument 1 passed to wcctaCar::__construct() must be an object, string given, called in ...
เราควรแก้ไขการทดสอบของเราด้วย...
ยอดเยี่ยม! มาสร้างการทดสอบสำหรับโรงงานของเรากันดีกว่า เราจะปล่อยให้วิธีการไม่มีเนื้อหาใด ๆ ในตอนนี้ ทำการทดสอบอีกครั้ง!
There was 1 risky test:
1) testswcctaFactoryTest::test_create
This test did not perform any assertions
การทดสอบผ่านไป แต่คุณได้รับข้อความว่ามีการทดสอบที่มีความเสี่ยง โดยวิธีการ: ตั้งชื่อฟังก์ชัน test_create
เพียงแค่ create
และใช้คำอธิบายประกอบ @test
ฉันเชื่อว่าการใช้คำอธิบายประกอบนั้นขึ้นอยู่กับรสนิยมส่วนตัวของคุณ!
ตอนนี้เราจะเจาะลึกเรื่องนี้อีกเล็กน้อย
สร้างอินเทอร์เฟซ FooterInterface
ที่กำหนด info
วิธีการสาธารณะซึ่งจะไม่คาดหวังค่าส่งคืนใดๆ ใช้อินเทอร์เฟซใน Car
info
สามารถแสดงข้อความตลกได้
กำหนดประเภทการส่งคืน FooterInterface
สำหรับ create
-method ของ Factory
และเพิ่ม info
-method ของ Car
ให้กับ WordPress-Action wp_footer
ตอนนี้เรามาทดสอบสิ่งนี้ใน FactoryTest
มีอย่างน้อยสองวิธีในการทดสอบอย่างถูกต้อง ใช้ has_action หรือ ActionsexpectAdded()
การทดสอบตัวกรองจะคล้ายกันและมีคำอธิบายไว้อย่างดีในหน้าที่เชื่อมโยง
ตรวจสอบว่า composer test
ยังผ่านการทดสอบทั้งหมดหรือไม่
ความคุ้มครองตอนนี้เป็นยังไงบ้าง? ดำเนินการ composer coverage
และตรวจสอบผลลัพธ์ที่สร้างขึ้น
info
วิธีการของ Car
ของเราไม่ครอบคลุมอยู่ในการทดสอบใดๆ แต่เราสามารถทดสอบผลลัพธ์ของวิธีการได้หรือไม่?
ปรากฎว่ามันค่อนข้างง่ายด้วย ExpectOutputString
มาเฉลิมฉลองสิ่งที่เราเรียนรู้กันเถอะ!
สร้างคลาส Locale
ที่มีเมธอดสาธารณะ get
ที่ส่งคืน get_locale()
ไม่รวมวิธีการครอบคลุม!
ตอนนี้สร้าง Constructor ใน Plugin
-class ของเราที่ยอมรับ Locale
-instance และเก็บไว้ใน member-var $this->locale
สร้างเมธอด get_region_code
ที่ส่งคืนค่าของ $this->locale->get()
อ่าและลบ is_loaded
-method ออก -
ในการทดสอบของเรา เราสามารถสร้างออบเจ็กต์ประเภท Locale
จำลองฟังก์ชัน WordPress get_locale
แล้วส่งต่อไปยัง Plugin
-constructor! แต่ฉันต้องการใช้ Mocker ที่นี่:
public function test_get_region_code() {
$code = 'it_IT';
$locale = Mockery::mock( Locale::class );
$locale->shouldReceive( 'get' )->andReturn( $code );
$sut = new Plugin( $locale );
$this->assertEquals( $code, $sut->get_region_code() );
}
ตอนนี้คุณสามารถไปและทำให้ปลั๊กอิน WordPress ของคุณกันกระสุนได้แล้ว!
มีความสุข!