Junit @ Before / @ After เรียกลำดับอะไร


134

ฉันมีชุดทดสอบการผสานรวม ฉันมีIntegrationTestBaseชั้นเรียนสำหรับการทดสอบทั้งหมดของฉันที่จะขยาย คลาสฐานนี้มีเมธอด@Before( public void setUp()) และ@After( public void tearDown()) เพื่อสร้างการเชื่อมต่อ API และ DB สิ่งที่ฉันได้ทำเป็นเพียงการเอาชนะทั้งสองวิธีการในแต่ละ testcase และเรียกและsuper.setUp() super.tearDown()อย่างไรก็ตามสิ่งนี้อาจทำให้เกิดปัญหาได้หากมีคนลืมโทรหาซุปเปอร์หรือวางไว้ผิดที่และมีข้อยกเว้นเกิดขึ้นและพวกเขาลืมโทรหาซุปเปอร์ในที่สุดหรือบางอย่าง

สิ่งที่ฉันต้องการทำคือสร้างsetUpและtearDownวิธีการในคลาสพื้นฐานfinalจากนั้นเพิ่มคำอธิบายประกอบ@Beforeและ@Afterวิธีการของเราเอง ทำการทดสอบเบื้องต้นดูเหมือนว่าจะเรียกตามลำดับนี้เสมอ:

Base @Before
Test @Before
Test
Test @After
Base @After

แต่ฉันกังวลเล็กน้อยว่าจะไม่มีการรับประกันคำสั่งซื้อและอาจทำให้เกิดปัญหาได้ ฉันมองไปรอบ ๆ และไม่เห็นอะไรเกี่ยวกับเรื่องนี้ ไม่มีใครรู้ว่าฉันสามารถทำได้และไม่มีปัญหาใด ๆ ?

รหัส:

public class IntegrationTestBase {

    @Before
    public final void setUp() { *always called 1st?* }

    @After
    public final void tearDown() { *always called last?* }
}


public class MyTest extends IntegrationTestBase {

    @Before
    public final void before() { *always called 2nd?* }

    @Test
    public void test() { *always called 3rd?* }

    @After
    public final void after() { *always called 4th?* }
}

1
ที่MyTestหายไปextendsคืออะไร?
aioobe

@aioobe: ไม่อีกแล้ว :)
Joel

คำตอบ:


136

ใช่พฤติกรรมนี้รับประกัน:

@Before:

@Beforeวิธีการ superclasses จะถูกเรียกใช้ก่อนที่บรรดาของระดับปัจจุบันจนกว่าพวกเขาจะแทนที่ในระดับปัจจุบัน ไม่มีการกำหนดลำดับอื่น ๆ

@After:

@Afterวิธีการประกาศใน superclasses จะทำงานหลังจากที่บรรดาของระดับปัจจุบันจนกว่าพวกเขาจะแทนที่ในระดับปัจจุบัน


15
เพื่อความชัดเจน@Beforeไม่รับประกันลำดับการดำเนินการของวิธีการทั้งหมด หากมี 10 @Beforeวิธีแต่ละวิธีสามารถดำเนินการตามลำดับใดก็ได้ ก่อนวิธีอื่น ๆ
Swati

5
ดังนั้นแทนที่จะอ้างถึงเอกสารที่ค่อนข้างคลุมเครือคุณช่วยอธิบายด้วยคำพูดของคุณเองได้ไหม มี@Beforeและ@Afterวิธีการทำงานก่อนที่ทุกวิธีการเรียนอื่น ๆ (หนึ่งครั้งต่อวิธีการ) หรือเพียงแค่ก่อนและหลังทั้งชุดของวิธีการเรียน (ครั้งเดียวต่อชั้น)?
BT

5
ดูสิ่งสำคัญที่ระบุโดย John Q Citizen: "สิ่งนี้ใช้ได้ก็ต่อเมื่อแต่ละวิธีที่มีเครื่องหมาย @Before มีชื่อเฉพาะในลำดับชั้นของคลาส" สำคัญมากที่ต้องจำ!
Bruno Bossola

ผมมีความขัดแย้งชื่อใช้ชื่อวิธีเดียวกันในวิธี @Before (ง) junit-4.12ในชั้นเรียนและวิธีอื่นในชั้นซุปเปอร์ของตนใน
Stephane

กฎนี้ใช้กับเมธอด @BeforeExample ของ ConcordionRunner ด้วยหรือไม่
Adrian Pronk

51

Gotcha ที่มีศักยภาพอย่างหนึ่งที่เคยกัดฉันมาก่อน:

ฉันชอบที่จะมีมากที่สุดหนึ่ง@Beforeวิธีในแต่ละคลาสการทดสอบเนื่องจาก@Beforeไม่รับประกันลำดับการรันเมธอดที่กำหนดไว้ในคลาส setUpTest()โดยปกติผมจะเรียกวิธีการดังกล่าว

แต่แม้ว่าจะ@Beforeมีการจัดทำเป็นเอกสารThe @Before methods of superclasses will be run before those of the current class. No other ordering is defined.แต่จะใช้ได้เฉพาะเมื่อแต่ละวิธีที่ทำเครื่องหมายด้วย@Beforeมีชื่อเฉพาะในลำดับชั้นของคลาส

ตัวอย่างเช่นฉันมีสิ่งต่อไปนี้:

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

ฉันคาดว่าAbstractFooTest.setUpTest()จะวิ่งมาก่อนFooTest.setUpTest()แต่FooTest.setupTest()ถูกประหารชีวิตเท่านั้น AbstractFooTest.setUpTest()ไม่ได้ถูกเรียกเลย

ต้องแก้ไขโค้ดดังต่อไปนี้จึงจะใช้งานได้:

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

ทำไมไม่เปลี่ยนชื่อของเมธอด @Before ในคลาสพื้นฐาน สิ่งนี้จะช่วยให้คุณไม่ต้องโทรไปหาซุปเปอร์ในเด็ก ๆ ทุกคน ... อย่างไรก็ตามการจับได้ดีกับปัญหาชื่อเดียวกัน
Lawrence Tierney

24
เพียงแค่คำพูดเพื่อทำให้สิ่งต่าง ๆ ปลอดภัยยิ่งขึ้น: เพื่อหลีกเลี่ยงการปะทะกันของชื่อคุณสามารถสร้าง@Before/ @Aftermethod (s) ในคลาสพื้นฐานได้finalดังนั้นคอมไพเลอร์จะบ่นหากคุณ (บังเอิญ) พยายามที่จะลบล้างสิ่งเหล่านี้ในคลาสย่อย
Stefan Winkler

4
เมธอดพาเรนต์ของชื่อเดียวกันที่ไม่ถูกเรียกใช้ไม่เหมือนกับลักษณะการทำงานของ JUnit ฟังดูเหมือนการทำงานของ Over-riding ขั้นพื้นฐานใน OOP โดยทั่วไปเมธอดหลักไม่มีอยู่ในขณะรันไทม์ เด็กจะแทนที่ด้วยความตั้งใจและวัตถุประสงค์ทั้งหมด นั่นคือวิธีการทำงานของ Java
Brandon

1
gotcha อีกอย่างคือคลาสพาเรนต์ต้องเป็นแบบสาธารณะมิฉะนั้น@Beforeเมธอดที่ทำเครื่องหมายไว้จะถูกละเว้นหากคลาสย่อยมี@Beforeเมธอดด้วย
rusins

21

ฉันคิดว่าตามเอกสารของ@Beforeและ@Afterข้อสรุปที่ถูกต้องคือการตั้งชื่อเฉพาะของเมธอด ฉันใช้รูปแบบต่อไปนี้ในการทดสอบของฉัน:

public abstract class AbstractBaseTest {

  @Before
  public final void baseSetUp() { // or any other meaningful name
    System.out.println("AbstractBaseTest.setUp");
  }

  @After
  public final void baseTearDown() { // or any other meaningful name
    System.out.println("AbstractBaseTest.tearDown");
  }
}

และ

public class Test extends AbstractBaseTest {

  @Before
  public void setUp() {
    System.out.println("Test.setUp");
  }

  @After
  public void tearDown() {
    System.out.println("Test.tearDown");
  }

  @Test
  public void test1() throws Exception {
    System.out.println("test1");
  }

  @Test
  public void test2() throws Exception {
    System.out.println("test2");
  }
}

ให้เป็นผล

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

ข้อดีของแนวทางนี้: ผู้ใช้คลาส AbstractBaseTest ไม่สามารถแทนที่เมธอด setUp / tearDown โดยบังเอิญ หากต้องการก็ต้องรู้ชื่อที่แน่นอนและทำได้

ข้อเสีย (เล็กน้อย) ของแนวทางนี้: ผู้ใช้ไม่สามารถมองเห็นว่ามีสิ่งต่างๆเกิดขึ้นก่อนหรือหลังการ setUp / tearDown พวกเขาจำเป็นต้องรู้ว่าสิ่งเหล่านี้จัดเตรียมโดยคลาสนามธรรม แต่ฉันคิดว่านั่นเป็นเหตุผลว่าทำไมพวกเขาถึงใช้คลาสนามธรรม


2
ตัวอย่างที่ดี - จะเป็นภาพประกอบได้มากขึ้นถ้าคุณมี @Test สองวิธีดังนั้นจะเห็นได้ว่า setUp และ tearDown ห่อหุ้มวิธีทดสอบแต่ละวิธี
ทำเครื่องหมาย

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

2

หากคุณพลิกผันคุณสามารถประกาศบทคัดย่อคลาสพื้นฐานของคุณและให้ลูกหลานประกาศเมธอด setUp และ tearDown (โดยไม่มีคำอธิบายประกอบ) ที่เรียกในเมธอด setUp และ tearDown ที่มีคำอธิบายประกอบของคลาสพื้นฐาน


1
ไม่ใช่ความคิดที่ไม่ดี แต่ฉันไม่ต้องการบังคับใช้สัญญากับการทดสอบที่ไม่จำเป็นต้องมีการตั้งค่า / การฉีกขาดของตนเอง
Joel

2

คุณสามารถใช้@BeforeClassคำอธิบายประกอบเพื่อรับรองว่าsetup()จะถูกเรียกก่อนเสมอ ในทำนองเดียวกันคุณสามารถใช้@AfterClassคำอธิบายประกอบเพื่อให้แน่ใจว่าtearDown()จะเรียกว่าสุดท้ายเสมอ

นี้มักจะไม่แนะนำ แต่มันจะได้รับการสนับสนุน

ไม่ใช่สิ่งที่คุณต้องการ แต่โดยพื้นฐานแล้วจะทำให้การเชื่อมต่อ DB ของคุณเปิดตลอดเวลาที่การทดสอบของคุณกำลังทำงานอยู่จากนั้นจึงปิดทันทีและทุกครั้งในตอนท้าย


2
อันที่จริงถ้าคุณจะทำเช่นนี้ฉันขอแนะนำให้สร้างวิธีการsetupDB()และcloseDB()ทำเครื่องหมายด้วย@BeforeClassและ@AfterClassและแทนที่วิธีการก่อน / หลังของคุณด้วยsetup()และtearDown()
Swati

วิธีการที่มีคำอธิบายประกอบ@BeforeClassและ@AfterClassจำเป็นต้องคงที่ แล้วกรณีนี้เมื่อเราต้องการใช้ตัวแปรอินสแตนซ์ภายในวิธีการเหล่านี้ล่ะ?
Pratik Singhal

คำเตือนเมื่อใช้@BeforeClassกับ Powermock: ใช้ได้กับการทดสอบครั้งแรกเท่านั้น ดูฉบับนี้: github.com/powermock/powermock/issues/398
Dagmar

2

นี่ไม่ใช่คำตอบสำหรับคำถามแท็กไลน์ แต่เป็นคำตอบสำหรับปัญหาที่กล่าวถึงในเนื้อหาของคำถาม แทนที่จะใช้ @Before หรือ @After ให้พิจารณาใช้@ org.junit.Ruleเพราะให้ความยืดหยุ่นมากกว่า ExternalResource (ณ 4.7) เป็นกฎที่คุณจะสนใจมากที่สุดหากคุณกำลังจัดการการเชื่อมต่อ นอกจากนี้หากคุณต้องการรับประกันการดำเนินการตามกฎของคุณให้ใช้RuleChain (ณ วันที่ 4.10) ฉันเชื่อว่าสิ่งเหล่านี้สามารถใช้ได้เมื่อมีการถามคำถามนี้ ตัวอย่างโค้ดด้านล่างนี้คัดลอกมาจาก Javadocs ของ ExternalResource

 public static class UsesExternalResource {
  Server myServer= new Server();

  @Rule
  public ExternalResource resource= new ExternalResource() {
      @Override
      protected void before() throws Throwable {
          myServer.connect();
         };

      @Override
      protected void after() {
          myServer.disconnect();
         };
     };

  @Test
  public void testFoo() {
      new Client().run(myServer);
     }
 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.