การฉีดการพึ่งพาจำลองอัตโนมัติสำหรับการทดสอบ
injected Trait
ช่วยให้คุณสร้างคลาสได้อย่างง่ายดายโดยมีการเยาะเย้ยการพึ่งพาทั้งหมดเพื่อการทดสอบ
รูปแบบต่อไปนี้เป็นเรื่องธรรมดามาก:
A
ที่มีการขึ้นต่อกัน (วัตถุบริการ) ที่ส่งผ่านตัวสร้างA
แต่ละรายการ โดยยืนยันว่าถูกเรียกตามที่คาดไว้ ตรรกะการทดสอบส่วนใหญ่กลายเป็นแบบสำเร็จรูปสำหรับการสร้างวัตถุ injected Trait
มีเป้าหมายที่จะลบรูปแบบสำเร็จรูปนี้ออกทั้งหมด เพื่อให้คุณมุ่งเน้นไปที่สิ่งที่สำคัญ: ตัวการทดสอบเอง
ใช้ composer
เพื่อรับรุ่นล่าสุด (คุณอาจจะต้องใช้เพื่อการพัฒนาเท่านั้น):
$ composer require sellerlabs/ injected --dev
สมมติว่าเรากำลังพัฒนาเว็บแอป และเราต้องการส่งอีเมลถึงผู้ใช้เมื่อพวกเขาสมัครใช้งาน สำหรับตัวอย่างง่ายๆ สมมติว่าผู้ใช้ถูกกำหนดโดยที่อยู่อีเมลของตนทั้งหมด เมื่อพวกเขาสมัคร แน่นอนว่าเราต้องการส่งอีเมลขอบคุณให้พวกเขา นอกจากนี้ เราต้องการทดสอบว่าอีเมลถูกส่งจริงๆ โดยไม่ได้ส่งจริงๆ
ขั้นแรก เรามากำหนดบริการอีเมลกันก่อน:
class EmailService
{
public function email ( $ address , $ content )
{
// Send an email to $address with body $content
}
}
(ในแอปพลิเคชันจริง email
จะส่งอีเมลออกไป แต่เราไม่ได้เกี่ยวข้องกับการใช้งานที่นี่!)
เรามากำหนด UserController
ซึ่งจัดการกระบวนการสมัครใช้งานที่ง่ายมาก:
class UserController
{
private $ service ;
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
public function signUp ( $ emailAddress )
{
$ this -> service -> email ( $ emailAddress , ' Thanks for signing up! ' );
return $ emailAddress ;
}
}
ที่นี่ เราจัดเตรียมการพึ่งพา EmailService
ผ่านทาง Constructor และใช้ในระหว่างขั้นตอนการสมัครใช้งาน (เรียบง่ายอย่างไม่น่าเชื่อ)
เพื่อทดสอบคลาสนี้ เราจะต้องทำสิ่งใดสิ่งหนึ่งจากสองสิ่งนี้:
EmailService
โดยใช้บางอย่างเช่น Mockery
และตรวจสอบให้แน่ใจว่ามีการเรียก email
ด้วยอาร์กิวเมนต์ที่คาดหวัง injected Trait
ช่วยให้คุณบรรลุทางเลือกที่ 2 ได้อย่างไม่ลำบาก มาดูกันดีกว่า:
use SellerLabs injected injected Trait ;
/**
* Class injected Example
*
* // 1. These are helpful annotations for IDEs and language tools
* @property MockInterface $service
* @method UserController make()
*
* @author Benjamin Kovach <[email protected]>
*/
class injected Example extends PHPUnit_Framework_TestCase
{
// 2. Use our trait
use injected Trait;
// 3. Provide the name of the class to test
protected $ className = UserController::class;
public function testSignUp ()
{
// 4. Make a controller with mocked dependencies
$ controller = $ this -> make ();
$ address = ' [email protected] ' ;
// 5. We can access any mocked dependency of the class as a property
$ this -> service -> shouldReceive ( ' email ' )
-> withArgs (
[
$ address ,
' Thanks for signing up! '
]
);
$ result = $ controller -> signUp ( $ address );
$ this -> assertEquals ( $ address , $ result );
}
}
ทุกคลาสที่ใช้ injected Trait
จำเป็นต้องมีคุณสมบัติ $className
ซึ่งใช้เพื่อค้นหาคลาสที่กำลังทดสอบ injected Trait
จัดให้มีวิธีการสาธารณะวิธีเดียว make
ซึ่งสร้างอ็อบเจ็กต์ประเภทนี้ แต่จะจำลองการขึ้นต่อกันและบันทึกเป็นคุณสมบัติของคลาสทดสอบเอง
ดังนั้นใน testSignUp
เรากำลังสร้างคอนโทรลเลอร์โดยใช้ make()
ซึ่งทำให้เราสามารถเข้าถึงออบเจ็กต์ประเภท EmailService
จำลองที่เรียกว่า $service
นี่เป็นเพราะมันถูกกำหนดเช่นนั้นในตัวสร้างของ UserController
:
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
ในช่วงระยะเวลาของกรณีทดสอบ ตัวแปร $service
member จะถูกผูกไว้กับ EmailService
ที่จำลองนี้ ซึ่งช่วยให้เราสามารถคาดหวังเกี่ยวกับสิ่งที่เกิดขึ้นกับตัวแปรนั้นเมื่อมีการเรียกใช้วิธี signUp
ของคอนโทรลเลอร์ เราใช้ Mockery
เพื่อสร้างวัตถุที่เยาะเย้ย มีคำอธิบายประกอบบางส่วนในความคิดเห็นของชั้นเรียน ซึ่งช่วยในการเติมข้อความอัตโนมัติของ IDE สำหรับคลาสเหล่านี้ เนื่องจากคุณสมบัติจำลองได้รับการประกาศแบบไดนามิก
ตัวอย่างนี้อยู่ใน tests/ injected Example.php
รู้สึกอิสระที่จะแหย่!
ผลกระทบของคุณลักษณะนี้อาจดูค่อนข้างน้อย แต่เมื่อทำงานกับแอปพลิเคชันขนาดใหญ่ที่คลาสมีการขึ้นต่อกันหลายอย่าง ทำให้การทดสอบง่ายขึ้น มาก