จะไปเกี่ยวกับการทดสอบรหัสยกเลิกการฉีดได้อย่างไร


13

ดังนั้นฉันจึงมีรหัสชิ้นส่วนต่อไปนี้ที่ใช้งานได้ทั่วระบบของฉัน ขณะนี้เรากำลังเขียนการทดสอบหน่วยย้อนหลัง (ดีกว่าไม่เป็นอาร์กิวเมนต์ของฉัน) แต่ฉันไม่เห็นว่าสิ่งนี้จะทดสอบได้อย่างไร

public function validate($value, Constraint $constraint)
{
    $searchEntity = EmailAlertToSearchAdapter::adapt($value);

    $queryBuilder = SearcherFactory::getSearchDirector($searchEntity->getKeywords());
    $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
    $query = $adapter->setupBuilder()->build();

    $totalCount = $this->advertType->count($query);

    if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
        $this->context->addViolation(
            $constraint->message
        );
    }
}

แนวคิดนี้ควรใช้ได้กับทุกภาษา แต่ฉันใช้ PHP รหัสจะสร้างวัตถุแบบสอบถาม ElasticSearch โดยยึดตามSearchวัตถุซึ่งจะถูกสร้างขึ้นจากEmailAlertวัตถุ สิ่งเหล่านี้SearchและEmailAlertเป็นเพียงของ POPO

ปัญหาของฉันที่ฉันไม่เห็นว่าฉันสามารถเยาะเย้ยออกSearcherFactory(ซึ่งใช้วิธีการคง) หรือSearchEntityToQueryAdapterที่ต้องการผลที่ได้จากSearcherFactory::getSearchDirector และSearchอินสแตนซ์ ฉันจะฉีดสิ่งที่สร้างขึ้นจากผลลัพธ์ภายในวิธีการได้อย่างไร อาจมีรูปแบบการออกแบบบางอย่างที่ฉันไม่ทราบ?

ขอบคุณสำหรับความช่วยเหลือใด ๆ !


@DocBrown มันจะถูกใช้ภายในโทรภายใน$this->context->addViolation if
iLikeBreakfast

1
ต้องตาบอดแน่ ๆ
Doc Brown

ดังนั้น :: ทั้งหมดเป็นสถิตยศาสตร์?
Ewan

ใช่ใน PHP ::เป็นวิธีคงที่
Andy

@Ewan ใช่::เรียกวิธีการคงที่ในชั้นเรียน
iLikeBreakfast

คำตอบ:


11

มี posibilites บางอย่าง, วิธีการเยาะเย้ยstaticวิธีใน PHP, ทางออกที่ดีที่สุดที่ฉันได้ใช้เป็นห้องสมุดAspectMockซึ่งสามารถดึงผ่านนักแต่งเพลง (วิธีการเยาะเย้ยวิธีการคงที่ค่อนข้างเข้าใจได้จากเอกสาร)

อย่างไรก็ตามมันเป็นการแก้ไขในนาทีสุดท้ายสำหรับปัญหาที่ควรได้รับการแก้ไขในวิธีที่ต่างออกไป

หากคุณยังต้องการทดสอบเลเยอร์ที่รับผิดชอบในการเปลี่ยนข้อความค้นหามันมีวิธีที่รวดเร็วในการทำ

ฉันกำลังสมมติว่าตอนนี้validateวิธีการเป็นส่วนหนึ่งของบางคลาสการแก้ไขอย่างรวดเร็วมากซึ่งไม่ต้องการให้คุณแปลงการโทรคงที่เป็นอินสแตนซ์การโทรทั้งหมดคือการสร้างคลาสที่ทำหน้าที่เป็นผู้รับมอบฉันทะสำหรับวิธีการคงที่ของคุณ ซึ่งก่อนหน้านี้ใช้วิธีการคงที่

class EmailAlertToSearchAdapterProxy
{
    public function adapt($value)
    {
        return EmailAlertToSearchAdapter::adapt($value);
    }
}

class SearcherFactoryProxy
{
    public function getSearchDirector(array $keywords)
    {
        return SearcherFactory::getSearchDirector($keywords);
    }
}

class ClassWithValidateMethod
{
    private $emailProxy;
    private $searcherProxy;

    public function __construct(
        EmailAlertToSearchAdapterProxy $emailProxy,
        SearcherFactoryProxy $searcherProxy
    )
    {
        $this->emailProxy = $emailProxy;
        $this->searcherProxy = $searcherProxy;
    }

    public function validate($value, Constraint $constraint)
    {
        $searchEntity = $this->emailProxy->adapt($value);

        $queryBuilder = $this->searcherProxy->getSearchDirector($searchEntity->getKeywords());
        $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
        $query = $adapter->setupBuilder()->build();

        $totalCount = $this->advertType->count($query);

        if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
            $this->context->addViolation(
                $constraint->message
            );
        }
    }
}

มันสมบูรณ์แบบ! ไม่เคยแม้แต่จะนึกถึงพร็อกซี่ ขอบคุณ!
iLikeBreakfast

2
ฉันเชื่อว่า Michael Feather เรียกว่านี่เป็นเทคนิค "Wrap Static" ในหนังสือของเขา "การทำงานอย่างมีประสิทธิภาพด้วยรหัสมรดก"
RubberDuck

1
@RubberDuck ฉันไม่แน่ใจว่ามันถูกเรียกว่า proxy จริงๆหรือไม่ นั่นคือสิ่งที่ฉันได้รับเรียกมันตราบเท่าที่ฉันจำได้ว่าใช้ชื่อของนายเฟเธอร์น่าจะเหมาะสมกว่าฉันยังไม่ได้อ่านหนังสือ
Andy

1
ชั้นเรียนนั้นเป็น "พร็อกซี" อย่างแน่นอน เทคนิคการทำลายการอ้างอิงเรียกว่า "การตัดแบบคงที่" IIRC ฉันขอแนะนำหนังสือ เต็มไปด้วยอัญมณีอย่างที่คุณให้ไว้ที่นี่
RubberDuck

5
หากงานของคุณเกี่ยวข้องกับการเพิ่มการทดสอบหน่วยลงในรหัสแล้ว "การทำงานกับรหัสดั้งเดิม" เป็นหนังสือที่แนะนำอย่างยิ่ง คำจำกัดความของเขาคือ "รหัสดั้งเดิม" คือ "รหัสที่ไม่มีการทดสอบหน่วย" ทั้งเล่มเป็นกลยุทธ์ในการเพิ่มการทดสอบหน่วยลงในรหัสที่ยังไม่ผ่านการทดสอบ
Eterm

4

ก่อนอื่นฉันขอแนะนำให้แบ่งออกเป็นวิธีการแยก:

public function validate($value, Constraint $constraint)
{
    $totalCount = QueryTotal($value);
    ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint);
}

private function QueryTotal($value)
{
    $searchEntity = EmailAlertToSearchAdapter::adapt($value);

    $queryBuilder = SearcherFactory::getSearchDirector($searchEntity->getKeywords());
    $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
    $query = $adapter->setupBuilder()->build();

    return $this->advertType->count($query);
}

private function ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint)
{
    if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
        $this->context->addViolation(
            $constraint->message
        );
    }
}

สิ่งนี้ทำให้คุณอยู่ในสถานการณ์ที่คุณสามารถพิจารณาทำให้ทั้งสองวิธีใหม่เป็นสาธารณะและการทดสอบหน่วยQueryTotalและเป็นShowMessageWhenTotalExceedsMaximumรายบุคคล ตัวเลือกที่ทำงานได้จริงที่นี่ไม่ใช่การทดสอบหน่วยQueryTotalเลยเนื่องจากคุณจะทำการทดสอบการค้นหาแบบยืดหยุ่นเท่านั้น การเขียนการทดสอบหน่วยShowMessageWhenTotalExceedsMaximumควรจะง่ายและเหมาะสมกว่าเนื่องจากเป็นการทดสอบตรรกะทางธุรกิจของคุณ

อย่างไรก็ตามหากคุณต้องการทดสอบ "ตรวจสอบความถูกต้อง" โดยตรงให้พิจารณาส่งผ่านฟังก์ชันการสืบค้นตัวเองเป็นพารามิเตอร์ใน "ตรวจสอบความถูกต้อง" (ด้วยค่าเริ่มต้น$this->QueryTotal) ซึ่งจะช่วยให้คุณสามารถจำลองฟังก์ชันการสืบค้นได้ ฉันไม่แน่ใจว่าฉันได้รับไวยากรณ์ PHP ถูกต้องหรือไม่ดังนั้นในกรณีที่ฉันไม่ได้โปรดอ่านสิ่งนี้ว่า "รหัสเทียม":

public function validate($value, Constraint $constraint, $queryFunc=$this->QueryTotal)
{
    $totalCount =  $queryFunc($value);
    ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint);
}

ฉันชอบความคิดนี้ แต่ฉันต้องการเก็บรหัสให้เป็นวัตถุมากขึ้นแทนที่จะใช้วิธีการเช่นนี้
iLikeBreakfast

@iLikeBreakfast ที่จริงแล้ววิธีการนี้ดีไม่ว่าจะมีอะไรอีก วิธีการควรสั้นที่สุดและทำสิ่งหนึ่งและสิ่งหนึ่งที่ดี (ลุงบ๊อบโค้ดสะอาด ) สิ่งนี้ทำให้ง่ายต่อการอ่านทำความเข้าใจและทดสอบได้ง่ายขึ้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.