การฉีดการพึ่งพาจำลองอัตโนมัติสำหรับการทดสอบ
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 รู้สึกอิสระที่จะแหย่!
ผลกระทบของคุณลักษณะนี้อาจดูค่อนข้างน้อย แต่เมื่อทำงานกับแอปพลิเคชันขนาดใหญ่ที่คลาสมีการขึ้นต่อกันหลายอย่าง ทำให้การทดสอบง่ายขึ้น มาก