Junit - chạy phương pháp thiết lập một lần


119

Tôi thiết lập một lớp với một vài bài kiểm tra và thay vì sử dụng, @Beforetôi muốn có một phương thức thiết lập chỉ thực thi một lần trước tất cả các bài kiểm tra. Điều đó có thể với Junit 4.8 không?


1
Có một cái nhìn tại RunListener: stackoverflow.com/a/14773170/548473
Grigory Kislin

Câu trả lời:


205

Mặc dù tôi đồng ý với @assylias rằng việc sử dụng @BeforeClasslà một giải pháp cổ điển nhưng không phải lúc nào cũng thuận tiện. Phương thức được chú thích @BeforeClassphải là phương thức tĩnh. Nó rất bất tiện cho một số thử nghiệm cần trường hợp thử nghiệm. Ví dụ: Kiểm tra dựa trên mùa xuân sử dụng @Autowiredđể làm việc với các dịch vụ được xác định trong ngữ cảnh mùa xuân.

Trong trường hợp này, cá nhân tôi sử dụng setUp()phương pháp thông thường được chú thích bằng @Beforechú thích và quản lý cờ static(!) Tùy chỉnh của mình boolean:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}

10
Thêm vào bình luận của Kenny Cason về lý do tại sao nó phải tĩnh. Nó phải tĩnh vì JUnit khởi tạo một phiên bản mới của lớp thử nghiệm cho mỗi phương thức @Test. Biến cá thể sẽ được đặt lại thành giá trị mặc định (false) cho mỗi phiên bản nếu nó không tĩnh. Xem để biết thêm thông tin: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz

2
Điều này hoạt động ngoại trừ trường hợp setUp()phương thức nằm trong lớp cha - đã đăng câu trả lời bên dưới để cố gắng giải quyết vấn đề này.
Steve Chambers

4
Tôi ngần ngại khi nói điều này với một người có 84 nghìn đại diện, nhưng thực tế BeforeClass không trả lời câu hỏi: BeforeClass được chạy ở đầu mỗi lớp thử nghiệm. Nhưng OP đã yêu cầu một cái chạy "chỉ một lần trước tất cả các thử nghiệm". Giải pháp đề xuất của bạn có thể làm điều này, nhưng bạn sẽ phải làm cho tất cả các lớp học thử nghiệm của bạn mở rộng một "CommonTest" class ...
mike động vật gặm nhấm

1
@mikerodent, IMHO OP đã hỏi về tất cả các thử nghiệm trong trường hợp thử nghiệm của anh ấy, không phải tất cả các thử nghiệm tổng thể. Vì vậy, bình luận của bạn là ít liên quan. BTW, đừng lo lắng khi nói bất cứ điều gì với bất kỳ người nào ngay cả khi danh tiếng của người đó cao. Ít nhất đây là những gì tôi làm :). Và danh tiếng của tôi đã giảm đáng kể vào tháng 8 năm 2012 khi tôi trả lời câu hỏi.
AlexR

Không hoạt động đối với trường hợp của tôi, các biến được khởi tạo trong thiết lập được đặt lại sau mỗi lần kiểm tra, vì vậy việc khởi tạo chỉ một lần là vô nghĩa.
Aphax

89

Bạn có thể sử dụng các BeforeClasschú thích :

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}

12
Tôi không thể sử dụng điều này, tôi có e vài phương pháp thiết lập dựa trên các thành phần không tĩnh như getClass ()
Bober02

1
@ Bober02 BeforeClass thực sự cần phải tĩnh. Nếu bạn không thể sử dụng câu trả lời đó, câu trả lời khác cung cấp một cách giải quyết.
assylias

2
Chắc chắn bạn không thể sử dụng TheClassYouWant.classthay thế cho lệnh gọi getClass () của mình? Đây là thực tế Java: String.class.getName().
stolsvik


1
@mikerodent Tôi đã hiểu câu hỏi là "tất cả các bài kiểm tra trong lớp" - nhưng bạn nói đúng, nó có thể không phải là điều OP muốn.
assylias

29

JUnit 5 hiện có chú thích @BeforeAll:

Biểu thị rằng phương thức được chú thích phải được thực thi trước tất cả các phương thức @Test trong phân cấp lớp hoặc lớp hiện tại; tương tự như @BeforeClass của JUnit 4. Các phương thức như vậy phải tĩnh.

Các chú thích về vòng đời của JUnit 5 dường như cuối cùng đã hiểu đúng! Bạn có thể đoán chú thích nào có sẵn mà không cần nhìn (ví dụ: @BeforeEach @AfterAll)


6
Nó có cùng một vấn đề là @BeforeClass, nó cần phải được static. Giải pháp của IMO @ AlexR là tốt hơn.
zengr

@zengr có xu hướng đồng ý với bạn: như tôi đã nói với AlexR, giải pháp của anh ấy yêu cầu tất cả các lớp thử nghiệm phải phân lớp từ một lớp CommonTest nếu nó chỉ chạy một lần. Nhưng nó đơn giản đến mức có thể, và IMHO có lẽ bạn không nên sử dụng một giải pháp khung được cung cấp "lạ mắt" khi một cơ chế đơn giản có sẵn từ ngôn ngữ. Tất nhiên là trừ khi có lý do chính đáng. Ngoài ra, sử dụng một thứ đơn giản như của anh ấy, với tên loại tốt "does what it said on the thiếc", giúp dễ đọc.
mike gặm nhấm

Nói điều này, một lần nữa IMHO, dường như có nhiều lý do hơn cho việc có chú thích "AfterAll": sẽ rất khó và bắt buộc phải thiết lập một cơ chế để phát hiện khi tất cả các thử nghiệm đã được thực hiện. Ngược lại, tất nhiên, những người theo chủ nghĩa thuần túy có lẽ sẽ nói rằng bạn không bao giờ phải thực hiện "lần dọn dẹp cuối cùng", tức là mỗi "lần dọn dẹp" nên để tất cả tài nguyên ở trạng thái nguyên sơ ... và họ có thể đúng!
mike gặm nhấm

Điều này có hoạt động với Maven khi có nhiều mô-đun, mỗi mô-đun có các bài kiểm tra của chúng không?
Mark Boon

@mike loài gặm nhấm, trong trường hợp của tôi, việc thiết lập và chia nhỏ các tệp thử nghiệm trong hệ thống tệp trước / sau mỗi lần kiểm tra dường như dẫn đến các tệp bị tắc. Hiện tại, tôi đã độc lập với giải pháp của AlexR để thiết lập một lần. Tôi có hai cờ tĩnh, readySetup và dirty. setup () gọi cleanup () nếu trạng thái bẩn được phát hiện ban đầu hoặc nếu lỗi thiết lập dẫn đến trạng thái bẩn. Để dọn dẹp sau khi chạy thử nghiệm, tôi chạy lại chúng. Lộn xộn, không lý tưởng chút nào, không nằm trong quá trình xây dựng của chúng tôi. Vẫn đang tìm cách tốt hơn (jUnit 4.12).
Rebeccah

9

Khi setUp()ở trong lớp cha của lớp thử nghiệm (ví dụ: AbstractTestBasebên dưới), câu trả lời được chấp nhận có thể được sửa đổi như sau:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

Điều này sẽ hoạt động đối với một setUp()phương pháp không tĩnh duy nhất nhưng tôi không thể tạo ra một phương pháp tương đương tearDown()mà không lạc vào một thế giới phản chiếu phức tạp ... Tiền thưởng chỉ cho bất kỳ ai có thể!


3

Chỉnh sửa: Tôi vừa phát hiện ra trong khi gỡ lỗi rằng lớp cũng được khởi tạo trước mỗi bài kiểm tra. Tôi đoán chú thích @BeforeClass là tốt nhất ở đây.

Bạn cũng có thể thiết lập trên phương thức khởi tạo, lớp kiểm tra xét cho cùng một lớp. Tôi không chắc đó có phải là một thực hành xấu hay không vì hầu hết các phương pháp khác đều được chú thích, nhưng nó hoạt động. Bạn có thể tạo một hàm tạo như vậy:

public UT () {
    // initialize once here
}
@Test
// Some test here...

Ctor sẽ được gọi trước khi kiểm tra vì chúng không tĩnh.


0

Hãy thử giải pháp này: https://stackoverflow.com/a/46274919/907576 :

với @BeforeAllMethods/ @AfterAllMethodsannotation, bạn có thể thực thi bất kỳ phương thức nào trong lớp Test trong ngữ cảnh cá thể, nơi tất cả các giá trị được đưa vào đều có sẵn.


Dựa vào thư viện của bên thứ ba.
Andrew

0

Giải pháp bẩn của tôi là:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

Tôi sử dụng nó như một cơ sở cơ bản cho tất cả các testCases của tôi.


public class TestCaseExtended mở rộng TestCase {private static boolean isInitialized = false; private static TestCaseExtended caseExtended; private int serId; @Override public void setUp () ném Exception {super.setUp (); if (! isInitialized) {caseExtended = new TestCaseExtended (); caseExtended.loadSaveNewSerId (); caseExtended.emptyTestResultsDirectory (); isInitialized = true; }}
Obi Hai

0

Nếu bạn không muốn bắt buộc khai báo một biến được đặt và kiểm tra trên mỗi bài kiểm tra con, thì việc thêm điều này vào SuperTest có thể làm:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}

0

Tôi đã giải quyết vấn đề này như thế này:

Thêm vào lớp trừu tượng Cơ sở của bạn (ý tôi là lớp trừu tượng nơi bạn khởi tạo trình điều khiển của mình trong phương thức setUpDriver () ) phần mã này:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

Và bây giờ, nếu các lớp thử nghiệm của bạn mở rộng từ lớp trừu tượng Cơ sở -> phương thức setUpDriver () sẽ được thực thi trước @Test đầu tiên chỉ MỘT lần mỗi lần chạy.


0

Sử dụng phương thức @PostConstruct của Spring để thực hiện tất cả công việc khởi tạo và phương thức này chạy trước khi bất kỳ @Test nào được thực thi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.