ตกลงเพื่อแนะนำวิธีการที่ใช้เฉพาะระหว่างการทดสอบหน่วยหรือไม่


12

เมื่อเร็ว ๆ นี้ฉันกำลังใช้วิธีการจากโรงงาน TDDing วิธีการคือการสร้างวัตถุธรรมดาหรือวัตถุห่อในมัณฑนากร วัตถุตกแต่งอาจเป็นหนึ่งในหลายประเภทที่ขยาย StrategyClass

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

ฉันใช้รหัสใน PHP ดังนั้นฉันจึงสามารถใช้ext/Reflectionเพื่อหาคลาสของวัตถุที่ถูกห่อ แต่ดูเหมือนว่าฉันจะมีความซับซ้อนมากเกินไปและค่อนข้างกฎเกณฑ์ของ TDD

แต่ฉันตัดสินใจที่จะแนะนำgetClassName()ว่าจะส่งคืนชื่อคลาสของวัตถุเมื่อถูกเรียกจาก StrategyClass เมื่อเรียกจากมัณฑนากรอย่างไรก็ตามมันจะคืนค่าที่ส่งคืนโดยวิธีเดียวกันในวัตถุที่ตกแต่ง

รหัสบางอย่างเพื่อให้ชัดเจนยิ่งขึ้น:

interface StrategyInterface {
  public function getClassName();
}

abstract class StrategyClass implements StrategyInterface {
  public function getClassName() {
    return \get_class($this);
  }
}

abstract class StrategyDecorator implements StrategyInterface {

  private $decorated;

  public function __construct(StrategyClass $decorated) {
    $this->decorated = $decorated;
  }

  public function getClassName() {
    return $this->decorated->getClassName();
  }

}

และการทดสอบ PHPUnit

 /**
   * @dataProvider providerForTestGetStrategy
   * @param array $arguments
   * @param string $expected
   */
  public function testGetStrategy($arguments, $expected) {

    $this->assertEquals(
      __NAMESPACE__.'\\'.$expected,  
      $this->object->getStrategy($arguments)->getClassName()
    )
  }

//below there's another test to check if proper decorator is being used

จุดของฉันที่นี่คือ: มันตกลงที่จะแนะนำวิธีการดังกล่าวที่ไม่มีประโยชน์อื่น ๆ กว่าที่จะทำให้การทดสอบหน่วยง่ายขึ้น? อย่างใดมันก็ไม่รู้สึกถูกต้องสำหรับฉัน


อาจเป็นไปได้ว่าคำถามนี้จะช่วยให้คุณเข้าใจคำถามของคุณได้มากขึ้นโปรแกรมเมอร์ของคุณstackexchange.com/questions/231237/ …ฉันเชื่อว่ามันขึ้นอยู่กับการใช้งานและวิธีการเหล่านี้จะช่วยในการทดสอบหน่วยและแก้จุดบกพร่อง .
Clement Mark-Aaba

คำตอบ:


13

ความคิดของฉันคือไม่คุณไม่ควรทำอะไรเพียงเพราะมันจำเป็นสำหรับการ testability การตัดสินใจจำนวนมากที่คนทำมีประโยชน์ต่อความสามารถในการทดสอบและการทดสอบอาจเป็นประโยชน์หลัก แต่ก็ควรเป็นการออกแบบที่ดีสำหรับข้อดีอื่น ๆ นี่หมายความว่าคุณสมบัติที่ต้องการบางอย่างไม่สามารถทดสอบได้ อีกตัวอย่างหนึ่งคือเมื่อคุณจำเป็นต้องรู้ว่ารูทีนมีประสิทธิภาพเพียงใดตัวอย่างเช่น Hashmap ของคุณใช้ช่วงค่าแฮชที่กระจายอย่างเท่าเทียมกันไม่มีอะไรในอินเทอร์เฟซภายนอกที่จะบอกคุณได้ว่า

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


1 OP บรรยาย 'กล่องชัดเจน' การทดสอบหน่วยไม่ TDD ทดสอบคุณสมบัติ
Steven A. Lowe

1
ฉันเห็นจุดเริ่มต้นถึงแม้ว่าฉันค่อนข้างลังเลที่จะเพิ่มการทดสอบอัลกอริทึม StrategyClass เมื่อฉันต้องการที่จะทดสอบว่าวิธีการที่โรงงานทำมันเป็นงาน การแยกประเภทนี้ IMHO อีกเหตุผลหนึ่งที่ฉันต้องการหลีกเลี่ยงนั่นคือคลาสเฉพาะเหล่านี้ทำงานบนฐานข้อมูลดังนั้นการทดสอบพวกเขาจึงต้องการการเยาะเย้ย / การขัดถูเพิ่มเติม
Mchl

ในทางกลับกันและในแง่ของคำถามนี้: programmers.stackexchange.com/questions/86656/เมื่อเราแยกแยะ "TDD tests" จาก "การทดสอบหน่วย" สิ่งนี้กลายเป็นเรื่องที่สมบูรณ์แบบ (ยังคงเป็นระบบฐานข้อมูลแม้ว่า: P)
Mchl

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

5

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

วิธีที่ฉันชอบในการจัดการสิ่งนี้ (และฉันต้องขอโทษที่ฉันไม่สามารถแสดงวิธีการทำเช่นนี้ใน PHP เพราะฉันส่วนใหญ่รหัสในภาษา C สไตล์) คือการให้ฟังก์ชั่น 'ทดสอบ' ในแบบที่พวกเขาไม่ได้ สัมผัสกับโลกภายนอกโดยวัตถุเอง แต่สามารถเข้าถึงได้โดยวัตถุที่ได้รับ สำหรับวัตถุประสงค์ในการทดสอบฉันจะได้คลาสที่จัดการกับการโต้ตอบกับวัตถุที่ฉันต้องการทดสอบและให้หน่วยทดสอบใช้วัตถุนั้น ตัวอย่าง C ++ จะมีลักษณะดังนี้:

ระดับประเภทการผลิต:

class ProdObj
{
  ...
  protected:
     virtual bool checkCertainState();
};

ทดสอบผู้ช่วยระดับ:

class TestHelperProdObj : public ProdObj
{
  ...
  public:
     virtual bool checkCertainState() { return ProdObj::checkCertainState(); }
};

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


แนวทางที่น่าสนใจ ฉันต้องดูว่าฉันสามารถปรับตัวนี้ได้อย่างไร
Mchl

3

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

การทดสอบเป็นสิ่งสำคัญหากจำเป็นต้องเพิ่มสิ่งนั้นลงไป

แต่ลองใช้ทางเลือกอื่น ตัวเลือกการสะท้อนของคุณนั้นไม่ได้แย่ขนาดนั้นทั้งหมด คุณสามารถมี accessor เสมือนที่ได้รับการปกป้องในสิ่งที่คุณต้องการและสร้างคลาสที่ได้รับมาเพื่อทดสอบและยืนยัน บางทีคุณสามารถแบ่งชั้นเรียนของคุณและทดสอบระดับลีฟผลลัพธ์ การซ่อนวิธีทดสอบด้วยตัวแปรคอมไพเลอร์ในซอร์สโค้ดของคุณเป็นตัวเลือกด้วย (ฉันเพิ่งจะรู้ PHP ไม่แน่ใจว่าเป็นไปได้ใน PHP)

ในบริบทของคุณคุณสามารถตัดสินใจที่จะไม่ทดสอบองค์ประกอบที่เหมาะสมภายในมัณฑนากร แต่ทดสอบพฤติกรรมที่คาดหวังการตกแต่งควรจะมี สิ่งนี้อาจจะให้ความสำคัญกับพฤติกรรมของระบบที่คาดหวังมากกว่าและไม่มากตามข้อกำหนดทางเทคนิค (รูปแบบมัณฑนากรซื้อคุณจากมุมมองของการทำงานอย่างไร)


1

ฉันเป็นมือใหม่ TDD แน่นอน แต่ดูเหมือนว่าจะขึ้นอยู่กับวิธีการที่เพิ่มเข้ามา จากสิ่งที่ฉันเข้าใจเกี่ยวกับ TDD การทดสอบของคุณควร "ขับ" การสร้าง API ของคุณในระดับหนึ่ง

เมื่อมันตกลง

  • ตราบใดที่วิธีนี้ไม่ทำลาย encapsulation และให้บริการตามวัตถุประสงค์ที่สอดคล้องกับความรับผิดชอบของวัตถุ

เมื่อไม่เป็นไร

  • หากวิธีการนั้นดูเหมือนสิ่งที่จะไม่มีประโยชน์หรือไม่มีเหตุผลเกี่ยวกับวิธีการอินเทอร์เฟซอื่น ๆ อาจเป็นสิ่งที่ไม่น่าเชื่อหรือสับสน ณ จุดนั้นมันจะทำให้ฉันเข้าใจวัตถุนั้นมาก
  • ตัวอย่างของเจเรมี่ "... เมื่อคุณต้องการทราบว่าประสิทธิภาพบางอย่างของงานประจำตัวอย่างเช่น Hashmap ของคุณใช้ช่วงค่าแฮชที่กระจายอย่างเท่าเทียมกัน - ไม่มีสิ่งใดในอินเทอร์เฟซภายนอกที่จะบอกคุณได้"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.