Before และ After Suite Execution hook ใน jUnit 4.x


84

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

หมายเหตุ: เรากำลังใช้ maven 2 สำหรับงานสร้างของเรา ฉันได้ลองใช้ maven pre-& post-integration-testphases แล้ว แต่ถ้าการทดสอบล้มเหลว maven จะหยุดและไม่ทำงานpost-integration-testซึ่งก็ไม่ได้ช่วยอะไร


1
สำหรับการทดสอบการรวมคุณควรใช้ปลั๊กอิน maven-failafeแทน surefire การดำเนินการนี้จะไม่ข้ามไปpost-integration-testหากการทดสอบล้มเหลว ดูหน้าวิกินี้ด้วย
Chris H.

คุณสามารถแบ่งปันการใช้งานขั้นสุดท้ายได้หรือไม่?
vikramvi

คำตอบ:


114

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

package com.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {

    @BeforeClass
    public static void setUp() {
        System.out.println("setting up");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearing down");
    }

}

ดังนั้นTest1ชั้นเรียนของคุณจะมีลักษณะดังนี้:

package com.test;

import org.junit.Test;


public class Test1 {
    @Test
    public void test1() {
        System.out.println("test1");
    }

}

... และคุณสามารถจินตนาการว่ามันTest2ดูคล้ายกัน หากคุณวิ่งTestSuiteคุณจะได้รับ:

setting up
test1
test2
tearing down

ดังนั้นคุณจะเห็นว่าการตั้งค่า / การฉีกขาดจะทำงานก่อนและหลังการทดสอบทั้งหมดตามลำดับเท่านั้น

สิ่งที่จับได้: จะใช้ได้เฉพาะเมื่อคุณใช้ชุดทดสอบและไม่ได้เรียกใช้ Test1 และ Test2 เป็นการทดสอบ JUnit แต่ละรายการ คุณบอกว่าคุณใช้ maven และปลั๊กอิน maven surefire ชอบเรียกใช้การทดสอบทีละรายการไม่ใช่ส่วนหนึ่งของชุด ในกรณีนี้ฉันขอแนะนำให้สร้างซูเปอร์คลาสที่แต่ละคลาสทดสอบขยายออกไป จากนั้นซุปเปอร์คลาสจะมีเมธอด @BeforeClass และ @AfterClass ที่มีคำอธิบายประกอบ แม้ว่าจะไม่สะอาดเท่าวิธีการข้างต้น แต่ฉันคิดว่ามันจะได้ผลสำหรับคุณ

สำหรับปัญหาเกี่ยวกับการทดสอบที่ล้มเหลวคุณสามารถตั้งค่า maven.test.error.ignore เพื่อให้การสร้างดำเนินต่อไปในการทดสอบที่ล้มเหลว ไม่แนะนำให้ใช้เป็นแบบฝึกหัดต่อเนื่อง แต่ควรทำให้คุณทำงานได้จนกว่าการทดสอบทั้งหมดจะผ่าน สำหรับรายละเอียดเพิ่มเติมโปรดดูที่เอกสาร Maven แหง


2
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉันเมื่อฉันเข้าสู่ maven-surefire-plugin และสร้างรายการรวมที่ชี้ไปที่ชุดที่ฉันต้องการเรียกใช้
Jherico

2
สำหรับ JUnit 4.8.2 สิ่งนี้ไม่สามารถเล่นได้ดีกับการทดสอบแบบกำหนดพารามิเตอร์ วิธีการ @BeforeClass ของ Suite จะทำงานหลังจากวิธีการทดสอบ @ Parameterized.Parameters ป้องกันการพึ่งพาการตั้งค่าของ Suite
Anm

เพื่อตอบสนองตัวเองเมื่อใช้ @Theories การเรียกใช้เมธอด @DataPoints จะเรียกตามหลัง @BeforeClass ของ Suite
Anm

19
ขออภัยสำหรับ necro - แต่การเพิ่ม BeforeClass / AfterClass ไปยัง super class ไม่ได้ผลตามที่คาดไว้ - ยังคงถูกเรียกหลังจากการทดสอบแต่ละคลาสเสร็จสิ้น นี้มีไว้สำหรับลูกหลาน
Subu Sankara Subramanian

3
นี่ยังเป็นแนวทางที่ถูกต้องหรือไม่? คุณจะหลีกเลี่ยงความจำเป็นในการแจกแจงรายชื่อคลาสทดสอบในคำอธิบายประกอบ SuiteClasses ได้อย่างไร
Burhan Ali

34

เพื่อนร่วมงานของฉันแนะนำสิ่งต่อไปนี้: คุณสามารถใช้ RunListener ที่กำหนดเองและใช้เมธอด testRunFinished (): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org junit.runner.Result)

ในการลงทะเบียน RunListener เพียงกำหนดค่าปลั๊กอิน surefire ดังนี้: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.htmlส่วน "การใช้ผู้ฟังและผู้สื่อข่าวที่กำหนดเอง"

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


5
+1 นั่นเป็นวิธีแก้ปัญหาที่ใช้งานได้อย่างแรกที่ฉันเคยเห็นโดยไม่ต้องดูแลชั้นสวีทให้ยุ่งยาก!
Stefan Haberl


6

โดยใช้คำอธิบายประกอบคุณสามารถทำสิ่งนี้ได้:

import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;

class SomethingUnitTest {
    @BeforeClass
    public static void runBeforeClass()
    {

    }

    @AfterClass
    public static void runAfterClass()
    {  

    }

    @Before  
    public void setUp()
    {

    }

    @After
    public void tearDown()
    {

    }

    @Test
    public void testSomethingOrOther()
    {

    }

}

4
การตั้งค่าและการฉีกขาดจะต้องดำเนินการหนึ่งครั้งต่อการรัน สิ่งนี้จะช่วยได้ก็ต่อเมื่อการทดสอบทั้งหมดอยู่ในชั้นเรียนเดียว
sblundy

นี่เป็นการตั้งค่าชุดทดสอบแต่ละชุดเท่านั้นไม่ใช่ขั้นตอนการทดสอบทั้งหมด
Ben

3

ที่นี่เรา

  • อัปเกรดเป็น JUnit 4.5
  • เขียนคำอธิบายประกอบเพื่อติดแท็กแต่ละชั้นทดสอบหรือวิธีการที่ต้องการบริการที่ใช้งานได้
  • เขียนตัวจัดการสำหรับคำอธิบายประกอบแต่ละรายการซึ่งมีวิธีการแบบคงที่เพื่อใช้การตั้งค่าและการแยกบริการ
  • ขยาย Runner ตามปกติเพื่อค้นหาคำอธิบายประกอบเกี่ยวกับการทดสอบโดยเพิ่มวิธีการจัดการแบบคงที่ลงในห่วงโซ่การดำเนินการทดสอบตามจุดที่เหมาะสม

2

สำหรับ "หมายเหตุ: เราใช้ maven 2 สำหรับงานสร้างของเราฉันได้ลองใช้ขั้นตอนการทดสอบก่อนและหลังการรวมของ maven แล้ว แต่ถ้าการทดสอบล้มเหลว maven จะหยุดและไม่เรียกใช้การทดสอบหลังการรวม ซึ่งไม่ใช่ความช่วยเหลือ "

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


ใช่ปลั๊กอินป้องกันความผิดพลาดจะช่วยให้คุณสามารถระบุการตั้งค่าเฉพาะและการฉีกขาดได้ แม้ว่าฉันจะไม่คิดว่ามีความปลอดภัยในขณะที่โพสต์คำถามนี้
Jason Axelson

2

หากการทดสอบทั้งหมดของคุณอาจขยายคลาส "เทคนิค" และอยู่ในแพ็คเกจเดียวกันคุณสามารถทำเคล็ดลับเล็กน้อย:

public class AbstractTest {
  private static int nbTests = listClassesIn(<package>).size();
  private static int curTest = 0;

  @BeforeClass
  public static void incCurTest() { curTest++; }

  @AfterClass
  public static void closeTestSuite() {
      if (curTest == nbTests) { /*cleaning*/ }             
  }
}

public class Test1 extends AbstractTest {
   @Test
   public void check() {}
}
public class Test2 extends AbstractTest {
   @Test
   public void check() {}
}

โปรดทราบว่าโซลูชันนี้มีข้อเสียมากมาย:

  • ต้องทำการทดสอบทั้งหมดของแพ็คเกจ
  • ต้องซับคลาสเป็นคลาส "techincal"
  • คุณไม่สามารถใช้ @BeforeClass และ @AfterClass ในคลาสย่อยได้
  • หากคุณทำการทดสอบเพียงครั้งเดียวในแพ็คเกจการทำความสะอาดจะไม่เสร็จสิ้น
  • ...

สำหรับข้อมูล: listClassesIn () => คุณจะค้นหาคลาสย่อยทั้งหมดของคลาสที่กำหนดใน Java ได้อย่างไร?


1
นี่ไม่เป็นความจริงเท่าที่การทดสอบของฉันแสดง ฉันมีคลาสสุดยอดที่เริ่มการฝังตัวปลาแก้วในชั้นเรียนและปิดมันลงหลังเลิกเรียน ฉันมีคลาส 2 คลาสที่ต่อยอดมาจากซุปเปอร์คลาสนั้น beforeclass จะถูกดำเนินการก่อนที่จะรันการทดสอบที่กำหนดไว้ในแต่ละคลาส
Jonathan Morales Vélez

0

เท่าที่ฉันรู้ไม่มีกลไกในการทำสิ่งนี้ใน JUnit อย่างไรก็ตามคุณสามารถลอง subclassing Suite และแทนที่เมธอด run () ด้วยเวอร์ชันที่มี hooks


0

เนื่องจาก maven-surefire-plugin ไม่ได้เรียกใช้ Suite class ก่อน แต่ถือว่าชุดและคลาสทดสอบเหมือนกันดังนั้นเราจึงสามารถกำหนดค่าปลั๊กอินด้านล่างเพื่อเปิดใช้งานเฉพาะคลาส Suite และปิดใช้งานการทดสอบทั้งหมด Suite จะทำการทดสอบทั้งหมด

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <includes>
                    <include>**/*Suite.java</include>
                </includes>
                <excludes>
                    <exclude>**/*Test.java</exclude>
                    <exclude>**/*Tests.java</exclude>
                </excludes>
            </configuration>
        </plugin>

0

วิธีเดียวที่ฉันคิดว่าจะได้ฟังก์ชั่นที่คุณต้องการก็คือทำสิ่งที่ชอบ

import junit.framework.Test;  
import junit.framework.TestResult;  
import junit.framework.TestSuite;  

public class AllTests {  
    public static Test suite() {  
        TestSuite suite = new TestSuite("TestEverything");  
        //$JUnit-BEGIN$  
        suite.addTestSuite(TestOne.class);  
        suite.addTestSuite(TestTwo.class);  
        suite.addTestSuite(TestThree.class);  
        //$JUnit-END$  
     }  

     public static void main(String[] args)  
     {  
        AllTests test = new AllTests();  
        Test testCase = test.suite();  
        TestResult result = new TestResult();  
        setUp();  
        testCase.run(result);  
        tearDown();  
     }  
     public void setUp() {}  
     public void tearDown() {}  
} 

ฉันใช้อะไรแบบนี้ในคราสดังนั้นฉันไม่แน่ใจว่ามันพกพาได้แค่ไหนนอกสภาพแวดล้อมนั้น


3
นี่คือตัวอย่างของ JUnit3 และ OP ขอ JUnit4 แต่ในกรณีที่ผู้ใช้ JUnit3 บางคนพบคำถามนี้ ... สำหรับ JUnit3 มันจะดีกว่าถ้ากำจัด main () method และมี suite () method ห่อ TestSuite ในคลาสย่อยของ junit.extensions.TestSetup คุณยังคงมีข้อแม้เช่นเดียวกับตัวอย่างของ Julie เกี่ยวกับการเรียกใช้คลาสทดสอบแต่ละชั้นใน IDE
NamshubWriter

0

หากคุณไม่ต้องการสร้างชุดและต้องแสดงรายการคลาสทดสอบทั้งหมดของคุณคุณสามารถใช้การสะท้อนกลับเพื่อค้นหาจำนวนคลาสทดสอบแบบไดนามิกและนับถอยหลังในคลาสพื้นฐาน @AfterClass เพื่อทำการ tearDown เพียงครั้งเดียว:

public class BaseTestClass
{
    private static int testClassToRun = 0;

    // Counting the classes to run so that we can do the tear down only once
    static {
        try {
            Field field = ClassLoader.class.getDeclaredField("classes");
            field.setAccessible(true);

            @SuppressWarnings({ "unchecked", "rawtypes" })
            Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
            for (Class<?> clazz : classes) {
                if (clazz.getName().endsWith("Test")) {
                    testClassToRun++;
                }
            }
        } catch (Exception ignore) {
        }
    }

    // Setup that needs to be done only once
    static {
        // one time set up
    }

    @AfterClass
    public static void baseTearDown() throws Exception
    {
        if (--testClassToRun == 0) {
            // one time clean up
        }
    }
}

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

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

แรงบันดาลใจมาจากคำตอบ SO นี้https://stackoverflow.com/a/37488620/5930242

หากคุณไม่ต้องการขยายคลาสนี้ไปทุกที่คำตอบสุดท้ายนี้อาจทำในสิ่งที่คุณต้องการ

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