@BeforeClass และการสืบทอด - ลำดับการดำเนินการ


93

ฉันมีคลาสพื้นฐานนามธรรมซึ่งฉันใช้เป็นฐานสำหรับการทดสอบหน่วยของฉัน (TestNG 5.10) ในคลาสนี้ฉันเตรียมใช้งานสภาพแวดล้อมทั้งหมดสำหรับการทดสอบการตั้งค่าการแมปฐานข้อมูล ฯลฯ คลาสนามธรรมนี้มีวิธีการที่มี@BeforeClassคำอธิบายประกอบซึ่งจะทำการเริ่มต้น

ต่อไปฉันจะขยายคลาสนั้นด้วยคลาสเฉพาะซึ่งฉันมี@Testวิธีการและ@BeforeClassวิธีการ วิธีการเหล่านี้ทำการกำหนดค่าเริ่มต้นเฉพาะคลาสของสภาพแวดล้อม (เช่นใส่บันทึกบางส่วนลงในฐานข้อมูล)

ฉันจะบังคับใช้คำสั่งเฉพาะของ@BeforeClassวิธีการที่มีคำอธิบายประกอบได้อย่างไร ฉันต้องการคนที่มาจากคลาสฐานนามธรรมเพื่อดำเนินการก่อนคลาสขยาย

ตัวอย่าง:

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @BeforeClass
    doSpecificInitialization() {...}

    @Test
    doTests() {...}
}

คำสั่งซื้อที่คาดหวัง:

A.doInitialization
B.doSpecificInitialization
B.doTests

ลำดับจริง:

B.doSpecificInitialization // <- crashes, as the base init is missing
(A.doInitialization        // <---not executed
 B.doTests)                // <-/

คำตอบ:


50

อย่าใส่@BeforeClassในabstractชั้นเรียน เรียกมันจากแต่ละคลาสย่อย

abstract class A {
    void doInitialization() {}
}

class B extends A {
    @BeforeClass
    void doSpecificInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

ดูเหมือนว่า TestNG มี@BeforeClass(dependsOnMethods={"doInitialization"})- ลองดูสิ


10
นั่นคือสิ่งที่ฉันต้องการหลีกเลี่ยง: ไม่จำเป็นต้องเรียกเมธอดของคลาส super (นามธรรม) อย่างชัดเจน โดยเฉพาะอย่างยิ่งเมื่อฉันมีคลาสซึ่งสืบทอดมาจาก A แต่ไม่มีเมธอด @BeforeClass ของตัวเอง ฉันต้องใส่อันเดียวเพื่อจุดประสงค์นั้น
Dominik Sandjaja

5
dependsOnMethodsวิธีแก้ปัญหาได้เคล็ดลับ แม้ว่าฉันจะชอบแนวทาง "superclass first" ...
Dominik Sandjaja

1
ในการใช้ "dependOnMethod" ไม่ควร "doInitialization" ด้วย "@Test"? นั่นเป็นปัญหาเนื่องจากในทางเทคนิคไม่ใช่การทดสอบด้วยตัวเอง ...
N3da

@BeforeClass ควรใส่คำอธิบายประกอบวิธีแบบคงที่
Fabrizio Stellato

110

แก้ไข:คำตอบด้านล่างสำหรับJUnitแต่ฉันจะปล่อยไว้ที่นี่ต่อไปเพราะอาจเป็นประโยชน์

ตามAPI ของ JUnit : "เมธอด @BeforeClass ของ superclasses จะถูกเรียกใช้ก่อนคลาสปัจจุบันเหล่านั้น"

ฉันทดสอบแล้วและดูเหมือนว่าจะได้ผลสำหรับฉัน

อย่างไรก็ตามตามที่ @Odys กล่าวถึงด้านล่างสำหรับ JUnit คุณต้องมีสองเมธอดที่ตั้งชื่อแตกต่างกันแม้ว่าการทำอย่างอื่นจะส่งผลให้มีการรันเฉพาะเมธอดคลาสย่อยเท่านั้นเนื่องจากพาเรนต์จะถูกเงา


56
แม้ว่าคำถามเดิมจะเป็นของ TestNG แต่ฉันก็มาถึงที่นี่หลังจาก googling สำหรับ JUnit และคำตอบของคุณช่วยได้ - ขอบคุณ!
กาน้ำชา

10
สำหรับ JUnit คุณต้องมีสองเมธอดที่ตั้งชื่อแตกต่างกันแม้ว่าการทำอย่างอื่นจะส่งผลให้มีการรันเฉพาะเมธอดคลาสย่อยเท่านั้นเนื่องจากพาเรนต์จะถูกเงา
Odys

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

คุณทำให้วันของฉัน ขอบคุณ!
raiks

7

ฉันเพิ่มลงpublicในคลาสนามธรรมและ TestNG (6.0.1) ดำเนินการ doInitialization () ก่อนหน้าdoTestsนี้ TestNG ไม่ดำเนินการdoInitialization()ถ้าฉันลบออกpublicจากคลาส A

public abstract class A {
 @BeforeClass
 doInitialization() {...}
}

class B extends A {    
 @Test
 doTests() {...}
}

1
นั่นเป็นเรื่องจริง แต่ไม่เกี่ยวข้อง นี้ไม่ได้ทำงานเมื่อเรียนB นอกจากนี้ยังมี@BeforeClassวิธีการ -annotated เช่นในกรณีของ OP
jpaugh

1
ฉันทำเช่นเดียวกัน ดูเหมือนว่าจะพลาดลำดับการสืบทอดหากวิธีพื้นฐานเป็นแบบส่วนตัว ขอบคุณ!
Manu

6

ฉันเพิ่งลองตัวอย่างของคุณด้วย 5.11 และฉันได้รับ @BeforeClass ของคลาสพื้นฐานที่เรียกใช้ก่อน

คุณสามารถโพสต์ไฟล์ testng.xml ของคุณได้หรือไม่ บางทีคุณอาจระบุทั้ง A และ B ที่นั่นในขณะที่จำเป็นต้องใช้ B เท่านั้น

อย่าลังเลที่จะติดตามรายชื่ออีเมลของผู้ใช้ทดสอบและเราสามารถตรวจสอบปัญหาของคุณได้อย่างละเอียดยิ่งขึ้น

- เซดริก


2
ไม่มี. xml สำหรับ testng ที่กำหนดไว้ (อย่างชัดเจน) มันทำงานจาก Eclipse และ Maven
Dominik Sandjaja

คุณเรียกใช้จาก Eclipse ได้อย่างไร? คลิกขวาที่คลาส B?
Cedric Beust

4

ฉันเพิ่งผ่านสิ่งนี้ไปและพบอีกหนึ่งวิธีในการบรรลุเป้าหมายนี้ เพียงใช้alwaysRunใน@BeforeClassหรือ@BeforeMethodในคลาสนามธรรมก็ได้ผลตามที่คุณคาดหวัง

public class AbstractTestClass {
    @BeforeClass(alwaysRun = true)
    public void generalBeforeClass() {
        // do stuff
        specificBeforeClass();
    }
}

2

เมื่อฉันเรียกใช้จาก: JUnitCore.runClasses (TestClass.class); มันจะดำเนินการพาเรนต์อย่างถูกต้องก่อนลูก (คุณไม่จำเป็นต้องใช้super.SetUpBeforeClass (); ) หากคุณเรียกใช้จาก Eclipse: ด้วยเหตุผลบางประการจึงไม่สามารถรันคลาสฐานได้ วิธีแก้ปัญหา: เรียกคลาสพื้นฐานอย่างชัดเจน: ( BaseTest.setUpBeforeClass (); ) คุณอาจต้องการมีแฟล็กในคลาสพื้นฐานในกรณีที่คุณเรียกใช้จากแอปพลิเคชันเพื่อตรวจสอบว่ามีการตั้งค่าแล้วหรือไม่ ดังนั้นมันจะทำงานเพียงครั้งเดียวถ้าคุณรันผ่านทั้งสองวิธีที่เป็นไปได้ (เช่นจาก eclipse สำหรับการทดสอบส่วนบุคคลและผ่าน ANT สำหรับรุ่นสร้าง)

สิ่งนี้ดูเหมือนจะเป็นจุดบกพร่องของ Eclipse หรืออย่างน้อยก็เกิดผลลัพธ์ที่ไม่คาดคิด ..


2

สำหรับ JUnit : ดังที่ @fortega ได้กล่าวไว้: ตาม API ของ JUnit: "วิธีการ @BeforeClass ของ superclasses จะถูกเรียกใช้ก่อนคลาสปัจจุบันเหล่านั้น"

แต่ต้องระวังไม่ให้ชื่อทั้งสองวิธีมีชื่อเดียวกัน เนื่องจากในกรณีนี้ผู้ปกครองเด็กจะซ่อนเมธอดหลัก แหล่ง


1

วิธีการที่มีวิธีการ @BeforeClass ของคุณเรียกเมธอด specificBeforeClass () ที่ว่างเปล่าซึ่งอาจหรือไม่อาจถูกเขียนทับโดยคลาสย่อยดังนี้:

public class AbstractTestClass {
  @BeforeClass
  public void generalBeforeClass() {
    // do stuff
    specificBeforeClass();
  }

  protected void specificBeforeClass() {}
}

public class SpecificTest {
  @Override
  protected void specificBeforeClass() {
    // Do specific stuff
  }

  // Tests
}

3
BeforeClass ต้องเป็นแบบคงที่คุณจึงไม่สามารถทำสิ่งนี้กับ junit ได้
madx

1

dependsOnMethod สามารถใช้ได้.

เช่นในกรณีของ Spring ( AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")

1

ตรวจสอบใบแจ้งการนำเข้าของคุณ มันควรจะเป็น

import org.testng.annotations.BeforeClass;

ไม่

import org.junit.BeforeClass;


1

สิ่งนี้ใช้ได้กับฉัน -

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @Override
    @BeforeClass
    doInitialization() { 

       //do class specific init

    }   

    @Test
    doTests() {...}
}

1
เพิ่มคำอธิบายสั้น ๆ ว่ารหัสนี้ใช้ทำอะไรและตอบคำถามเดิม
Cray

0

ทำไมคุณไม่ลองสร้างเมธอดนามธรรม doSpecialInit () ในซูเปอร์คลาสของคุณซึ่งเรียกจากเมธอดที่มีคำอธิบายประกอบ BeforeClass ของคุณในซูเปอร์คลาส

ดังนั้นผู้พัฒนาที่สืบทอดคลาสของคุณจึงถูกบังคับให้ใช้วิธีนี้


พูดตามตรงว่าตรรกะอาจเปลี่ยนไปในช่วง 3 1/2 ปีที่ผ่านมาตั้งแต่ฉันถามคำถามนี้ ... ;-) ใช่บางทีนี่อาจเป็นความคิดบางทีมันอาจจะไม่ได้ผล - ฉันไม่สุจริต จำไว้.
Dominik Sandjaja

0

มีอีกวิธีง่ายๆที่นี่

สถานการณ์เฉพาะของฉันคือฉันต้องฉีดบริการจำลองจาก "BeforeClass" ในคลาสย่อยก่อนที่ "BeforeClass" ในซูเปอร์คลาสจะทำงาน

ในการทำสิ่งนี้ - เพียงแค่ใช้@ClassRuleในคลาสย่อย

ตัวอย่างเช่น:

@ClassRule
public static ExternalResource mocksInjector = new ExternalResource() {
    @Override
    protected void before() {
        // inject my mock services here
        // Note: this is executed before the parent class @BeforeClass
    }
};

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


0

วันนี้ฉันประสบปัญหาคล้าย ๆ กันข้อแตกต่างเพียงอย่างเดียวคือคลาสฐานไม่ใช่นามธรรม

นี่คือกรณีของฉัน

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    private void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

เกิดขึ้นที่@BeforeClassเมธอดจากคลาส A ไม่เคยถูกเรียกใช้

  • A. doInitialization () -> สิ่งนี้ไม่เคยดำเนินการอย่างเงียบ ๆ
  • B. doSpecificInitialization ()
  • ข. doTests ()

การเล่นกับตัวปรับแต่งความเป็นส่วนตัวฉันพบว่า TestNG จะไม่เรียกใช้@BeforeClassวิธีการที่มีคำอธิบายประกอบจากคลาสที่สืบทอดมาหากไม่สามารถมองเห็นวิธีการจากผู้สืบทอดคลาส

สิ่งนี้จะได้ผล:

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    //Here a privacy modifier matters -> please make sure your method is public or protected so it will be visible for ancestors
    protected void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

ผลที่ตามมาเกิดขึ้น:

  • ก. doInitialization ()
  • B. doSpecificInitialization ()
  • ข. doTests ()

-1

ในกรณีของฉัน (JUnit) ฉันมีวิธีการเดียวกันที่เรียกว่าการตั้งค่า () ในคลาสฐานและคลาสที่ได้รับ ในกรณีนี้จะเรียกเฉพาะเมธอดของคลาสที่ได้รับและฉันเรียกมันว่าเมธอดคลาสฐาน


-2

วิธีที่ดีกว่าและสะอาดกว่าในการบรรลุสิ่งนี้โดยใช้การสืบทอดอาจมีดังต่อไปนี้ -

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

    @Override
    @BeforeClass
    void doInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

1
หากคุณต้องการให้เมธอดในคลาส Parent ถูกเรียกใช้งานก่อนอื่นคุณต้องตั้งชื่อเมธอดในคลาส Child ให้แตกต่างจาก Parent's (เพราะถ้าพวกมันมีลายเซ็นเหมือนกันความหลากหลายจะเกิดขึ้น) ฉันเชื่อว่ามันเป็นวิธีที่สะอาดกว่า
Anatolii Stepaniuk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.