แพ็คเกจนี้ช่วยให้รันการทดสอบหน่วยที่เกี่ยวข้องกับเวลา/IO (เช่น การเรียกใช้ฟังก์ชันสลีป การสืบค้นฐานข้อมูล การเรียก API ฯลฯ) ได้เร็วขึ้นโดยใช้ Swoole
แพคเกจ counit อนุญาตให้รันการทดสอบที่เกี่ยวข้องกับ IO หลายครั้งพร้อมกันภายในกระบวนการ PHP เดียวโดยใช้ Swoole Counit เข้ากันได้กับ PHPUnit ซึ่งหมายความว่า:
กรณีทดสอบทั่วไปของ counit มีลักษณะดังนี้:
use Deminy Counit TestCase ; // Here is the only change made for counit, comparing to test cases for PHPUnit.
class SleepTest extends TestCase
{
public function testSleep (): void
{
$ startTime = time ();
sleep ( 3 );
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
}
}
เมื่อเปรียบเทียบกับ PHPUnit แล้ว counit จะทำให้กรณีทดสอบของคุณเร็วขึ้น นี่คือการเปรียบเทียบเมื่อรันชุดทดสอบเดียวกันโดยใช้ PHPUnit และ counit สำหรับโปรเจ็กต์จริง ในชุดทดสอบ การทดสอบจำนวนมากทำการเรียกเมธอด DeminyCounitCounit::sleep() เพื่อรอบางสิ่งที่จะเกิดขึ้น (เช่น รอให้ข้อมูลหมดอายุ)
# ของการทดสอบ | #คำยืนยัน | ถึงเวลาที่จะเสร็จสิ้น | |
---|---|---|---|
counit (ไม่มี Swoole) หรือ PHPUnit | 44 | 1148 | 9 นาที 18 วินาที |
counit (เปิดใช้งาน Swoole) | 19 วินาที |
สามารถติดตั้งแพ็คเกจได้โดยใช้ Composer :
composer require deminy/counit --dev
หรือในไฟล์ composer.json ของคุณ ตรวจสอบให้แน่ใจว่าได้รวมแพ็คเกจ deminy/counit ไว้ด้วย:
{
"require-dev" : {
"deminy/counit" : " ~0.2.0 "
}
}
โฟลเดอร์ ./tests/unit/global และ ./tests/unit/case-by-case มีตัวอย่างการทดสอบบางส่วน โดยที่เรามีการทดสอบที่เกี่ยวข้องกับเวลาดังต่อไปนี้:
หากต้องการรันการทดสอบตัวอย่าง โปรดเริ่มคอนเทนเนอร์ Docker และติดตั้งแพ็คเกจ Composer ก่อน:
docker-compose up -d
docker compose exec -ti swoole composer install -n
มีคอนเทนเนอร์เริ่มต้นอยู่ห้าคอนเทนเนอร์: คอนเทนเนอร์ PHP, คอนเทนเนอร์ Swoole, คอนเทนเนอร์ Redis, คอนเทนเนอร์ MySQL และเว็บเซิร์ฟเวอร์ คอนเทนเนอร์ PHP ไม่ได้ติดตั้งส่วนขยาย Swoole ในขณะที่คอนเทนเนอร์ Swole ได้ติดตั้งและเปิดใช้งานแล้ว
ตามที่กล่าวไว้ก่อนหน้านี้ กรณีทดสอบสามารถเขียนได้ในลักษณะเดียวกับกรณีของ PHPUnit อย่างไรก็ตาม เพื่อให้การทดสอบที่เกี่ยวข้องกับเวลา/IO เร็วขึ้นด้วย counit เราจำเป็นต้องทำการปรับเปลี่ยนบางอย่างเมื่อเขียนกรณีทดสอบเหล่านั้น การปรับเปลี่ยนเหล่านี้สามารถทำได้ในสองสไตล์ที่แตกต่างกัน
ในลักษณะนี้ แต่ละกรณีทดสอบจะทำงานในโครูทีนแยกกันโดยอัตโนมัติ
สำหรับกรณีทดสอบที่เขียนในรูปแบบนี้ การเปลี่ยนแปลงเพียงอย่างเดียวที่ต้องทำในกรณีทดสอบที่มีอยู่ของคุณคือการใช้คลาส DeminyCounitTestCase แทน PHPUnitFrameworkTestCase เป็นคลาสพื้นฐาน
กรณีทดสอบทั่วไปของสไตล์สากลมีลักษณะดังนี้:
use Deminy Counit TestCase ; // Here is the only change made for counit, comparing to test cases for PHPUnit.
class SleepTest extends TestCase
{
public function testSleep (): void
{
$ startTime = time ();
sleep ( 3 );
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
}
}
เมื่อมีการกำหนดเมธอดที่กำหนดเอง setUpBeforeClass() และ TearDownAfterClass() ในกรณีทดสอบ โปรดตรวจสอบให้แน่ใจว่าได้เรียกเมธอดหลักตามเมธอดที่ปรับแต่งเหล่านี้
สไตล์นี้จะถือว่าไม่มีการยืนยันทันทีในกรณีทดสอบ หรือการยืนยันก่อนการเรียกใช้ฟังก์ชัน sleep() หรือการดำเนินการ IO ที่เป็นมิตรกับโครูทีน กรณีทดสอบเช่นต่อไปนี้ยังคงใช้งานได้ แต่จะทำให้เกิดข้อความเตือนเมื่อทำการทดสอบ:
class GlobalTest extends Deminy Counit TestCase
{
public function testAssertionSuppression (): void
{
self :: assertTrue ( true , ' Trigger an immediate assertion. ' );
// ......
}
}
เราสามารถเขียนคลาสการทดสอบนี้ใหม่โดยใช้สไตล์ "case by case" (จะกล่าวถึงในหัวข้อถัดไป) เพื่อกำจัดข้อความเตือน
หากต้องการค้นหาการทดสอบเพิ่มเติมที่เขียนในรูปแบบนี้ โปรดตรวจสอบการทดสอบภายใต้โฟลเดอร์ ./tests/unit/global (ชุดทดสอบ "global")
ในรูปแบบนี้ คุณจะทำการเปลี่ยนแปลงโดยตรงในกรณีทดสอบเพื่อให้ทำงานแบบอะซิงโครนัส
สำหรับกรณีทดสอบที่เขียนในรูปแบบนี้ เราจำเป็นต้องใช้คลาส DeminyCounitCounit ตามนั้นในกรณีทดสอบที่เราต้องรอการดำเนินการ PHP หรือดำเนินการ IO โดยทั่วไปแล้ว การเรียกเมธอดต่อไปนี้จะถูกนำมาใช้:
กรณีทดสอบทั่วไปของสไตล์แต่ละกรณีมีลักษณะดังนี้:
use Deminy Counit Counit ;
use PHPUnit Framework TestCase ;
class SleepTest extends TestCase
{
public function testSleep (): void
{
Counit:: create ( function () { // To create a new coroutine manually to run the test case.
$ startTime = time ();
Counit:: sleep ( 3 ); // Call this method instead of PHP function sleep().
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
});
}
}
ในกรณีที่คุณต้องการระงับข้อความเตือน "การทดสอบนี้ไม่ได้ดำเนินการยืนยันใดๆ" หรือเพื่อให้จำนวนการยืนยันตรงกัน คุณสามารถรวมพารามิเตอร์ที่ 2 เมื่อสร้างโครูทีนใหม่:
use Deminy Counit Counit ;
use PHPUnit Framework TestCase ;
class SleepTest extends TestCase
{
public function testSleep (): void
{
Counit:: create ( // To create a new coroutine manually to run the test case.
function () {
$ startTime = time ();
Counit:: sleep ( 3 ); // Call this method instead of PHP function sleep().
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
},
1 // Optional. To suppress warning message "This test did not perform any assertions", and to make the counters match.
);
}
}
หากต้องการค้นหาการทดสอบเพิ่มเติมที่เขียนในรูปแบบนี้ โปรดตรวจสอบการทดสอบในโฟลเดอร์ ./tests/unit/case-by-case (ชุดการทดสอบ "case-by-case")
ที่นี่เราจะทำการทดสอบภายใต้สภาพแวดล้อมที่แตกต่างกัน โดยมีหรือไม่มี Swoole
#1
รันชุดทดสอบโดยใช้ PHPUnit :
# To run test suite "global":
docker compose exec -ti php ./vendor/bin/phpunit --testsuite global
# or,
docker compose exec -ti swoole ./vendor/bin/phpunit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti php ./vendor/bin/phpunit --testsuite case-by-case
# or,
docker compose exec -ti swoole ./vendor/bin/phpunit --testsuite case-by-case
#2
รันชุดทดสอบโดยใช้ counit (ไม่มี Swoole):
# To run test suite "global":
docker compose exec -ti php ./counit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti php ./counit --testsuite case-by-case
#3
รันชุดทดสอบโดยใช้ counit (โดยเปิดใช้งานส่วนขยาย Swoole):
# To run test suite "global":
docker compose exec -ti swoole ./counit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti swoole ./counit --testsuite case-by-case
คำสั่งสองชุดแรกใช้เวลาดำเนินการประมาณเท่ากัน ชุดคำสั่งสุดท้ายใช้ counit และรันในคอนเทนเนอร์ Swoole (โดยที่ส่วนขยาย Swoole ถูกเปิดใช้งาน) ดังนั้นจึงเร็วกว่าอันอื่น:
สไตล์ | # ของการทดสอบ | #คำยืนยัน | ถึงเวลาที่จะเสร็จสิ้น | |
---|---|---|---|---|
counit (ไม่มี Swoole) หรือ PHPUnit | ทั่วโลก | 16 | 24 | 48 วินาที |
เป็นรายกรณีไป | 48 วินาที | |||
counit (เปิดใช้งาน Swoole) | ทั่วโลก | 7 วินาที | ||
เป็นรายกรณีไป | 7 วินาที |
เนื่องจากแพ็คเกจนี้อนุญาตให้รันการทดสอบหลายรายการพร้อมกัน เราไม่ควรใช้ทรัพยากรเดียวกันในการทดสอบที่แตกต่างกัน มิฉะนั้นสภาพการแข่งขันอาจเกิดขึ้นได้ ตัวอย่างเช่น หากการทดสอบหลายรายการใช้คีย์ Redis เดียวกัน การทดสอบบางรายการอาจล้มเหลวในบางครั้ง ในกรณีนี้ เราควรใช้คีย์ Redis ที่แตกต่างกันในกรณีทดสอบที่แตกต่างกัน สามารถใช้เมธอด DeminyCounitHelper::getNewKey() และ DeminyCounitHelper::getNewKeys() เพื่อสร้างคีย์ทดสอบแบบสุ่มและไม่ซ้ำกัน
แพคเกจทำงานได้ดีที่สุดสำหรับการทดสอบที่มีการเรียกใช้ฟังก์ชัน sleep() ใช้งานอยู่ นอกจากนี้ยังสามารถช่วยให้รันการทดสอบที่เกี่ยวข้องกับ IO ได้เร็วขึ้น โดยมีข้อจำกัด นี่คือรายการข้อจำกัดของแพ็คเกจนี้:
มีอิมเมจ deminy/counit ที่สร้างไว้ล่วงหน้าสำหรับเรียกใช้การทดสอบตัวอย่าง ต่อไปนี้เป็นคำสั่งในการสร้างภาพ:
docker build -t deminy/counit:php-only -f ./dockerfiles/php/Dockerfile .
docker build -t deminy/counit:swoole-enabled -f ./dockerfiles/swoole/Dockerfile .
แพ็คเกจนี้อนุญาตให้ใช้ Swoole เพื่อรันการทดสอบที่เกี่ยวข้องกับ IO หลายครั้งโดยไม่ต้องมีการประมวลผลหลายตัว ซึ่งหมายความว่าการทดสอบทั้งหมดสามารถทำงานได้ภายในกระบวนการ PHP เดียว เพื่อให้เข้าใจถึงวิธีการทำงานอย่างชัดเจน ฉันขอแนะนำให้ตรวจสอบการพูดคุยออนไลน์ฟรีนี้: การเขียนโปรแกรม CSP ใน PHP (และนี่คือสไลด์)
ในระบบนิเวศของ PHP มีตัวเลือกอื่นในการรันการทดสอบหน่วยแบบขนาน ส่วนใหญ่จบลงด้วยการใช้การประมวลผลหลายตัว:
ใบอนุญาตเอ็มไอที