เริ่มต้นการทดสอบ PHP ของคุณด้วย Codeception
เผยแพร่แล้ว: 2022-03-11ก่อนที่จะไปยัง Codeception และ PHP เราควรครอบคลุมพื้นฐานและเริ่มต้นด้วยการอธิบายว่าเหตุใดเราจึงต้องการทดสอบในแอปพลิเคชันตั้งแต่แรก บางทีเราอาจจะทำโปรเจ็กต์ให้เสร็จได้โดยไม่เสียเวลากับการทดสอบ อย่างน้อยครั้งนี้
แน่นอนว่าคุณไม่จำเป็นต้องทำการทดสอบทุกอย่าง ตัวอย่างเช่น เมื่อคุณต้องการสร้างโฮมเพจอื่น คุณอาจไม่ต้องการการทดสอบเมื่อโปรเจ็กต์ของคุณมีเพจสแตติกที่เชื่อมโยงโดยเราเตอร์ตัวเดียว
อย่างไรก็ตาม คุณต้องทำการทดสอบอย่างแน่นอนเมื่อ:
- ทีมของคุณใช้ BDD/TDD
- Git repo ของคุณมีคอมมิตมากกว่าสองรายการ
- คุณเป็นมืออาชีพที่เหมาะสม ทำงานในโครงการที่จริงจัง
คุณสามารถแก้ตัวโดยบอกว่าคุณมีแผนกทดสอบเฉพาะอยู่แล้ว กลุ่มคนที่ทำแบบทดสอบและเขียนบทใหม่เมื่อจำเป็น แต่ลองนึกภาพออกไหมว่าการแก้ไขจุดบกพร่องจะใช้เวลานานเท่าใดหลังจากที่คุณเพิ่มฟังก์ชันการทำงานใหม่ให้กับโครงการของคุณ
การทดสอบช่วยแก้ปัญหาอะไร?
อันดับแรก มาตัดสินใจว่าปัญหาประเภทใดที่อาจแก้ไขได้ด้วยการทดสอบ คุณไม่สามารถกำจัดข้อผิดพลาดทั้งหมดด้วยการทดสอบได้ แต่คุณสามารถอธิบายลักษณะการทำงานที่คาดหวังในกรณีทดสอบได้ ข้อผิดพลาดอาจอยู่ในกรณีทดสอบของคุณ รหัสสปาเก็ตตี้ยังคงเป็นรหัสสปาเก็ตตี้แม้ว่าคุณจะใช้กรณีทดสอบ
อย่างไรก็ตาม คุณสามารถมั่นใจได้ว่าโค้ดของคุณจะถูกเปลี่ยนหลังจากนั้น (โดยการแก้ไขข้อผิดพลาดหรือเพิ่มคุณสมบัติใหม่) ดังนั้นโค้ดของคุณจะปราศจากข้อผิดพลาดที่อธิบายไว้ในการทดสอบ นอกจากนี้ แม้แต่การทดสอบที่เขียนอย่างดีในบางครั้งอาจถูกนำมาใช้ในเอกสารประกอบ เนื่องจากคุณสามารถดูสถานการณ์ทั่วไปที่เกิดขึ้นและตรวจสอบพฤติกรรมที่คาดหวังได้ เราสามารถพูดได้ว่าการทดสอบเป็นการลงทุนเพียงเล็กน้อยแต่มีความสำคัญในอนาคต
เราสามารถใช้การทดสอบประเภทใดได้บ้าง
- การทดสอบหน่วย: การทดสอบ ระดับต่ำที่ตรวจสอบโค้ดชิ้นเล็กๆ ของคุณ ซึ่งเป็นเมธอดของคลาสที่แยกได้จากโค้ดอื่นๆ
- การทดสอบเชิงบูรณาการ: การทดสอบเชิงบูรณาการจะตรวจสอบส่วนหนึ่งของแอปพลิเคชันของคุณ โดยอาจมีคลาสหรือเมธอดหลายคลาส แต่ควรจำกัดไว้เพียงคุณสมบัติเดียว การทดสอบนี้ควรตรวจสอบว่าคลาสต่างๆ มีการโต้ตอบกันอย่างไร
- การทดสอบการทำงาน: ทดสอบคำขอเฉพาะสำหรับแอปพลิเคชันของคุณ: การตอบสนองของเบราว์เซอร์ การเปลี่ยนแปลงฐานข้อมูล และอื่นๆ
- การทดสอบการยอมรับ: ในกรณีส่วนใหญ่ การทดสอบการยอมรับหมายถึงการตรวจสอบว่าแอปพลิเคชันตรงตามข้อกำหนดของลูกค้าทั้งหมดหรือไม่
เพื่อความกระจ่าง สมมติว่าเราแสดงกระบวนการด้วยสิ่งที่จับต้องได้ เช่น อาคาร อาคารประกอบด้วยบล็อกขนาดเล็กที่สร้างกำแพง อิฐแต่ละก้อนต้องเป็นไปตามข้อกำหนดที่กำหนด ต้องทนต่อน้ำหนักที่ต้องการ มีปริมาตรและรูปร่างเฉพาะ เป็นต้น นี่คือการทดสอบหน่วย แนวคิดของการทดสอบการรวมคือการตรวจสอบว่าอิฐยึดติดกันแน่นและแม่นยำเพียงใด ผสานเข้ากับองค์ประกอบบางอย่างของอาคารอย่างไร การทดสอบการใช้งานสามารถเปรียบได้กับการทดสอบบนผนังด้านเดียวของอาคาร เพื่อตรวจสอบว่าภายในได้รับการปกป้องจากองค์ประกอบต่างๆ หรือไม่ และสามารถมองเห็นดวงอาทิตย์ผ่านหน้าต่างได้หรือไม่ การทดสอบการยอมรับเกี่ยวข้องกับการทดสอบผลิตภัณฑ์ทั้งอาคารเป็นผลิตภัณฑ์ที่สมบูรณ์: เปิดประตู เข้าไปข้างใน ปิดประตู เปิดไฟ ปีนขึ้นไปที่ชั้นสองและชมสวนนอกอาคาร
พบกับ Codeception
อย่างไรก็ตาม หมวดนี้มี เงื่อนไข และบางครั้งก็ยากที่จะต้านทานการทดลองแบบต่างๆ ผสมกัน
นักพัฒนาหลายคนใช้การทดสอบหน่วยและอ้างว่าเพียงพอแล้ว ฉันเคยเป็นนักพัฒนาคนหนึ่ง ฉันพบว่าการใช้ระบบต่างๆ สำหรับการทดสอบประเภทต่างๆ ยากและใช้เวลานานเกินไป เมื่อไม่นานมานี้ ฉันตัดสินใจค้นหาสิ่งที่มีประโยชน์มากกว่า PHPUnit; ฉันต้องการทดสอบโค้ดของฉันให้ดีขึ้น แต่ฉันไม่ต้องการอ่านและเรียนรู้เอกสารจำนวนมากและมองหาข้อผิดพลาด นั่นเป็นวิธีที่ฉันค้นพบ Codeception ทีแรกก็สงสัยเหมือนกันว่ามีอะไรใหม่ๆ อยู่บ่อยๆ (โปรเจ็กต์นี้อายุ 5 ขวบแล้ว ในทางเทคนิคแล้ว เรียกว่า "ใหม่ไม่ได้") แต่หลังจากลองเล่นมาสองสามวันแล้ว ฉันสรุปว่า Codeception เป็นระบบที่มีประโยชน์และทรงพลังมาก
คุณจะติดตั้ง Codeception ได้อย่างไร? มันง่ายเหมือนที่ได้รับ:
$ composer require "codeception/codeception" $ php vendor/bin/codecept bootstrap
หลังจากติดตั้ง คุณจะพบโฟลเดอร์ใหม่ที่ชื่อว่า การทดสอบ ในโครงการของคุณ และจะมีโฟลเดอร์ย่อยบางโฟลเดอร์ชื่อ accept, functional และ unit ดูเหมือนว่าเราจะเริ่มเขียนแบบทดสอบของเราได้แล้ว เจ๋ง แต่จะทำอย่างไรต่อไป?
ตอนนี้ให้ลองเพิ่มการ ยอมรับการ ทดสอบมาตรฐาน Hello World
$ php vendor/bin/codecept generate:cept acceptance HelloWorld
ตอนนี้ เราได้รับไฟล์การทดสอบการยอมรับการทดสอบ/acceptance/HelloWorldCept.php โดยมีเนื้อหาดังต่อไปนี้:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result');
ตัวแปรเริ่มต้นชื่อ $I
ไม่ใช่แค่ตัวอักษร มันเป็นตัวละคร อะไรดำเนินการทดสอบ? ผู้ทดสอบเห็นได้ชัดว่า ผู้ทดสอบนี้เปิดหน้าหรือชั้นเรียนของเว็บไซต์ของคุณ ทำบางอย่างกับมัน และแสดงผลลัพธ์สุดท้ายของการดำเนินการ คุณจะเห็นว่าอะไรได้ผลและอะไรผิดพลาด นั่นเป็นสาเหตุที่ชื่อวัตถุนี้ $I
และเหตุใดจึงมีวิธีการที่เรียกว่า wantTo()
, see()
หรือ amOnPage()
ลองคิดเหมือนผู้ทดสอบเกี่ยวกับวิธีการตรวจสอบความสามารถในการทำงานของเพจ วิธีแรกคือการเปิดหน้าและค้นหาวลี เป็นการพิสูจน์ว่าหน้านี้มีให้สำหรับผู้เยี่ยมชม
ควรเป็นเรื่องง่าย:
<?php $I->amOnPage('/'); $I->see('Welcome');
เราสามารถใช้คำสั่งนี้เพื่อรันการทดสอบของ Codeception:
$ php vendor/bin/codecept run
เราจะเห็นได้ทันทีว่ามีบางอย่างผิดปกติ เมื่อมองแวบแรก ดูเหมือนว่าข้อความจะยาวเกินไปและไม่ชัดเจน แต่เมื่อเรามองให้ละเอียดมากขึ้น ทุกอย่างก็ชัดเจน
เรามีการทดสอบหนึ่งรายการ Acceptance และตรวจพบข้อผิดพลาด:
Acceptance Tests (1) Perform actions and see result (HelloWorldCept) Error ---------- 1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php) [GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
นี่คือผู้ร้าย: localhost ไม่พร้อมใช้งาน
และนี่คือขั้นตอนสถานการณ์สมมติของการทดสอบของเรา:
1. $I->amOnPage("/")
ตกลง มาเปิดการทดสอบ/acceptance.suite.yml และเปลี่ยน url: http://localhost/
เป็นสิ่งที่ใช้ได้จริง ในกรณีของฉันมันเป็นโฮสต์ทดสอบในพื้นที่ของฉัน url: https://local.codeception-article.com/
ทำการทดสอบอีกครั้งและนี่คือสิ่งที่คุณควรจะลงเอยด้วย:
Acceptance Tests (1) --------------------------------------------------------------------------------------- Perform actions and result (HelloWorldCept) Ok
ไชโย! การทดสอบที่ประสบความสำเร็จครั้งแรกของเรา!
แน่นอนว่า amOnPage()
ไม่ใช่วิธีการทดสอบเพียงวิธีเดียวที่มี เราเพียงแต่แยกแยะออกมาเพื่อเป็นตัวอย่างเท่านั้น วิธีการทดสอบ Codeception ทั้งหมดสามารถแบ่งออกเป็นกลุ่มต่อไปนี้:
- การโต้ตอบกับหน้า:
fillField()
, selectOption(selectOption()
,submitForm()
,click()
- การยืนยัน
see()
,dontSee()
,seeElement()
,seeInCurrentUrl()
,seeCheckboxIsChecked()
,seeInField()
,seeLink()
สำหรับวิธีการทั้งหมดเหล่านี้ คุณสามารถเพิ่มส่วนต่อท้ายและใช้งานได้เมื่อคุณต้องการวิธีที่จะไม่ขัดจังหวะสถานการณ์การทดสอบเมื่อไม่พบบางสิ่ง - วิธีการใช้คุกกี้:
setCookie()
,grabCookie()
,seeCookie()
- ความคิดเห็นและคำอธิบายของสถานการณ์การทดสอบ:
amGoingTo()
,wantTo()
,expect()
ใช้วิธีการเหล่านี้เพื่อให้ได้ความคิดเห็นที่ดีและอธิบายการทดสอบ ซึ่งจะช่วยให้คุณจำเป้าหมายของการทดสอบได้
ดังนั้น หากเราต้องทดสอบหน้าอีเมลรีเซ็ตรหัสผ่าน เราสามารถทำได้ดังนี้:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('Test forgotten password functionality'); $I->amOnPage('/forgotten') $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link not sent for incorrect email'); $I->see('Email is incorrect, try again'); $I->amGoingTo('Fill correct email and get link'); $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link sent for correct email'); $I->see('Please check your email for next instructions');
ดูเหมือนว่าควรทำเช่นนี้ แต่ถ้ามีส่วนที่โหลด Ajax อยู่ในหน้าล่ะ เราสามารถทดสอบหน้าแบบนั้นได้ไหม? คำตอบคือ Codeception ใช้ PhpBrowser ตาม Symfony BrowserKit และ Guzzle โดยค่าเริ่มต้น มันง่าย รวดเร็ว และคุณต้องการเพียงแค่ม้วนงอเพื่อใช้งาน

คุณยังสามารถใช้ซีลีเนียมและหน้าทดสอบกับเบราว์เซอร์จริงได้ ใช่ มันจะช้าลง แต่คุณจะสามารถทดสอบ JavaScript ได้เช่นกัน
ขั้นแรก คุณต้องติดตั้งโปรแกรมควบคุม Selenium เปลี่ยน accept.suite.yml และสร้างคลาส AcceptanceTester ใหม่ หลังจากนี้ คุณสามารถใช้เมธอด wait()
และ waitForElement()
ได้ และที่น่าสนใจกว่านั้น คุณจะสามารถประหยัดเวลาและทรัพยากรของคุณโดยใช้วิธีการ saveSessionSnapshot()
และ loadSessionSnapshot()
วิธีนี้ช่วยให้คุณเก็บสถานะเซสชันและเริ่มการทดสอบใหม่ในเซสชันก่อนหน้า สิ่งนี้มีประโยชน์ในบางสถานการณ์ เช่น ในกระบวนการอนุมัติการทดสอบ
ดังนั้นเราจึงลงเอยด้วยความสามารถที่เรียบง่ายแต่ทรงพลังในการทดสอบฟังก์ชันต่างๆ มากมาย
การทดสอบการทำงาน
ตกลง ได้เวลาไปยังการทดสอบการใช้งาน
$ php vendor/bin/codecept generate:cept functional HelloWorld
และนี่คือสิ่งที่เราได้รับ:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->see('Welcome');
รออะไร?
ไม่มันไม่ใช่ความผิดพลาด การทดสอบเชิงฟังก์ชันควรเขียนในลักษณะเดียวกับการทดสอบเชิงบูรณาการ ความแตกต่างคือการทดสอบการทำงานโต้ตอบกับแอปพลิเคชันของคุณโดยตรง ซึ่งหมายความว่าคุณไม่จำเป็นต้องมีเว็บเซิร์ฟเวอร์เพื่อเรียกใช้การทดสอบการทำงาน และคุณมีศักยภาพมากขึ้นสำหรับการทดสอบส่วนต่างๆ ของแอปพลิเคชันของคุณ
หมายความว่าขาดการรองรับเฟรมเวิร์กทั้งหมด แต่รายชื่อเฟรมเวิร์กที่รองรับนั้นมีมากมาย: Symfony, Silex, Phalcon, Yii, Zend Framework, Lumen, Laravel ซึ่งน่าจะเพียงพอสำหรับกรณีส่วนใหญ่และนักพัฒนาส่วนใหญ่ โปรดศึกษาเอกสารประกอบโมดูลของ Codeception เพื่อรับรายการฟังก์ชันที่พร้อมใช้งาน จากนั้นเพียงเปิดใน functional.suite.yml
ก่อนที่เราจะทำการทดสอบหน่วย ให้ฉันพูดนอกเรื่องเล็กน้อยก่อน อย่างที่คุณอาจสังเกตเห็น เราได้สร้างการทดสอบของเราด้วยแนวคิดหลัก:
$ php vendor/bin/codecept generate: cept acceptance HelloWorld
นี่ไม่ใช่วิธีเดียวในการสร้างการทดสอบ นอกจากนี้ยังมี การทดสอบ Cest ความแตกต่างคือ คุณสามารถจัดโครงสร้างสถานการณ์ที่เกี่ยวข้องได้หลายสถานการณ์ในคลาสเดียว:
$ php vendor/bin/codecept generate:cest acceptance HelloWorld <?php class HelloWorldCest { public function _before(AcceptanceTester $I) { $I->amOnPage('/forgotten') } public function _after(AcceptanceTester $I) { } // tests public function testEmailField(AcceptanceTester $I) { $I->see('Enter email'); } public function testIncorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Email is incorrect, try again'); } public function testCorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Please check your email for next instructions'); } }
ในตัวอย่างนี้ เมธอด _before()
และ _after()
จะถูกรันก่อนและหลังการทดสอบแต่ละครั้ง อินสแตนซ์ของคลาส AcceptanceTester
จะถูกส่งต่อไปยังการทดสอบแต่ละครั้ง ดังนั้นคุณจึงสามารถใช้คลาสนี้ในลักษณะเดียวกับการทดสอบระดับสูงสุด รูปแบบของการทดสอบนี้อาจมีประโยชน์ในบางสถานการณ์ ดังนั้นจึงควรคำนึงถึง
การทดสอบหน่วย
เวลาสำหรับการทดสอบหน่วยบางส่วน
Codeception ขึ้นอยู่กับ PHPUnit ดังนั้นคุณสามารถใช้การทดสอบที่เขียนขึ้นสำหรับ PHPUnit ในการเพิ่มการทดสอบ PHPUnit ใหม่ ให้ใช้วิธีการต่อไปนี้:
$ php vendor/bin/codecept generate:phpunit unit HelloWorld
หรือเพียงแค่รับช่วงการทดสอบของคุณบน \PHPUnit_Framework_TestCase
แต่ถ้าคุณต้องการอะไรมากกว่านี้ คุณควรลองใช้การทดสอบหน่วยของ Codeception:
$ php vendor/bin/codecept generate:test unit HelloWorld <?php class HelloWorldTest extends \Codeception\TestCase\Test { /** * @var \UnitTester */ protected $tester; protected function _before() { } protected function _after() { } // tests public function testUserSave() { $user = User::find(1); $user->setEmail('[email protected]'); $user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); } }
ไม่มีอะไรผิดปกติสำหรับตอนนี้ เมธอด _before()
และ _after()
เป็นแบบแอนะล็อก setUp( setUp()
และ tearDown()
และจะรันก่อนและหลังการทดสอบแต่ละครั้ง
ข้อได้เปรียบหลักของการทดสอบนี้คือความสามารถในการขยายกระบวนการทดสอบของคุณโดยรวมโมดูลที่สามารถเปิดได้ใน unit.suite.yml
:
- เข้าถึง memcache และฐานข้อมูลเพื่อติดตามการเปลี่ยนแปลง (รองรับ MySQL, SQLite, PostgreSQL, MongoDB)
- การทดสอบแอปพลิเคชัน REST/SOAP
- คิว
แต่ละโมดูลมีคุณสมบัติของตัวเอง ดังนั้นจึงควรตรวจสอบเอกสารประกอบและรวบรวมข้อมูลที่จำเป็นสำหรับแต่ละโมดูลก่อนดำเนินการทดสอบจริง
นอกจากนี้ คุณสามารถใช้แพ็คเกจ Codeception/Specify (ซึ่งจำเป็นต้องเพิ่มใน composer.json
) และเขียนคำอธิบายดังนี้:
<?php class HelloWorldTest extends \Codeception\TestCase\Test { use \Codeception\Specify; private $user; protected function _before() { $this->user = User::find(1); } public function testUserEmailSave() { $this->specify("email can be stored", function() { $this->user->setEmail('[email protected]'); $this->user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); }); } }
โค้ด PHP ภายในฟังก์ชันการปิดเหล่านี้ถูกแยกออก ดังนั้นการเปลี่ยนแปลงภายในจะไม่ส่งผลต่อโค้ดที่เหลือของคุณ คำอธิบายจะช่วยให้การทดสอบอ่านง่ายขึ้นและระบุการทดสอบที่ล้มเหลวได้ง่ายขึ้น
คุณสามารถใช้แพ็คเกจ Codeception\Verify
สำหรับไวยากรณ์ที่เหมือน BDD ได้:
<?php public function testUserEmailSave() { verify($map->getEmail())->equals('[email protected]'); }
และแน่นอนคุณสามารถใช้ต้นขั้วได้:
<?php public function testUserEmailSave() { $user = Stub::make('User', ['getEmail' => '[email protected]']); $this->assertEquals('[email protected]', $user->getEmail()); }
คำตัดสิน: Codeception ประหยัดเวลาและความพยายาม
คุณควรคาดหวังอะไรจาก Codeception? สำหรับใคร? มีข้อแม้ใด ๆ หรือไม่?
ในความคิดของฉัน เฟรมเวิร์กการทดสอบนี้เหมาะสำหรับทีมต่างๆ ทุกประเภท: ทั้งขนาดใหญ่และขนาดเล็ก ผู้เริ่มต้นใช้งานและผู้เชี่ยวชาญด้าน PHP ที่สู้รบเต็มที่ ผู้ที่ใช้เฟรมเวิร์กยอดนิยม และผู้ที่ไม่ได้ใช้เฟรมเวิร์กใดๆ
ไม่ว่าในกรณีใด ทั้งหมด ล้วนมีเหตุผล: Codeception พร้อมสำหรับช่วงเวลาไพร์มไทม์
เป็นเฟรมเวิร์กที่ครบถ้วนและมีเอกสารประกอบอย่างดี ซึ่งอาจขยายได้โดยโมดูลจำนวนมาก Codeception นั้นทันสมัย แต่อิงตาม PHPUnit ที่ทดสอบตามเวลา ซึ่งควรสร้างความมั่นใจให้กับนักพัฒนาที่ไม่ต้องการทดลองมากเกินไป
มันทำงานได้ดี ซึ่งหมายความว่ารวดเร็วและไม่ต้องใช้เวลาและความพยายามมากเกินไป ยังดีกว่า มันค่อนข้างง่ายในการเรียนรู้ และเอกสารมากมายควรช่วยในกระบวนการเรียนรู้ที่ไม่ยุ่งยาก
Codeception นั้นง่ายต่อการติดตั้งและกำหนดค่า แต่มีตัวเลือกขั้นสูงมากมาย แม้ว่าผู้ใช้ส่วนใหญ่จะไม่ต้องการทั้งหมด (หรือส่วนใหญ่) ก็ตาม แต่ทั้งหมดก็ขึ้นอยู่กับว่าคุณตั้งใจจะทำอะไรกับมัน คุณสามารถเริ่มต้นด้วยพื้นฐาน และคุณสมบัติพิเศษจะมีประโยชน์ไม่ช้าก็เร็ว