Junit trước lớp (không tĩnh)


84

Có bất kỳ phương pháp hay nhất nào để Junit thực thi một hàm một lần trong tệp thử nghiệm không và nó cũng không được tĩnh.

như @BeforeClasstrên chức năng không tĩnh?

Đây là một giải pháp xấu xí:

@Before void init(){
    if (init.get() == false){
        init.set(true);
        // do once block
    }
}

Đây là điều tôi không muốn làm và tôi đang tìm kiếm một giải pháp junit tích hợp.


Chà, tôi có một hệ thống phân cấp khá lớn các tệp thử nghiệm và tệp thử nghiệm cơ sở, tôi cần khả năng ghi đè hành động này trong các lớp thử nghiệm con.
La Mã

1
tôi đã gặp vấn đề tương tự, trong đó chỉ bài kiểm tra đầu tiên trong số nhiều bài kiểm tra tham số mới thực hiện đăng nhập.
dokaspar

5
Lưu ý rằng giải pháp "xấu xí", giải pháp hoạt động với JUnit đơn giản, không tính đến các thử nghiệm xé nhỏ.
eskatos

Câu trả lời:


22

Nếu bạn không muốn thiết lập trình khởi tạo tĩnh để khởi tạo một lần và không muốn sử dụng JUnit, hãy xem TestNG. TestNG hỗ trợ khởi tạo không tĩnh, một lần với nhiều tùy chọn cấu hình, tất cả đều sử dụng chú thích.

Trong TestNG, điều này sẽ tương đương với:

@org.testng.annotations.BeforeClass
public void setUpOnce() {
   // One time initialization.
}

Đối với giọt nước mắt,

@org.testng.annotations.AfterClass
public void tearDownOnce() {
   // One time tear down.
}

Đối với TestNG tương đương với JUnit 4 @Before@After, bạn có thể sử dụng @BeforeMethod@AfterMethodtương ứng.


41

Một câu lệnh if đơn giản dường như cũng hoạt động khá tốt:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class myTest {

    public static boolean dbInit = false;

    @Autowired
    DbUtils dbUtils;

    @Before
    public void setUp(){

        if(!dbInit){

            dbUtils.dropTables();
            dbUtils.createTables();
            dbInit = true;

        }
    }

 ...

1
Đẹp và đơn giản! Nhưng không thể thấy một cách đơn giản điều chỉnh điều này để tạo ra một @AfterClasstương đương không tĩnh có thể rơi xuống sau khi tất cả các thử nghiệm đã chạy?
Steve Chambers

1
Xem tại đây để biết bản cập nhật cho phương thức này sẽ hoạt động cho các lớp thử nghiệm sử dụng kế thừa.
Steve Chambers

36

Sử dụng một hàm tạo rỗng là giải pháp dễ dàng nhất. Bạn vẫn có thể ghi đè hàm tạo trong lớp mở rộng.

Nhưng nó không phải là tối ưu với tất cả các thừa kế. Đó là lý do tại sao JUnit 4 sử dụng chú thích thay thế.

Một tùy chọn khác là tạo một phương thức trợ giúp trong lớp factory / Prac và để phương thức đó thực hiện công việc.

Nếu đang sử dụng Spring, bạn nên cân nhắc sử dụng @TestExecutionListenerschú thích. Một cái gì đó giống như thử nghiệm này:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({CustomTestExecutionListener.class, 
     DependencyInjectionTestExecutionListener.class})
@ContextConfiguration("test-config.xml")
public class DemoTest {

Spring's AbstractTestExecutionListenerchứa ví dụ như phương thức trống này mà bạn có thể ghi đè:

public void beforeTestClass(TestContext testContext) throws Exception {
    /* no-op */
}

LƯU Ý: KHÔNG bỏ sót / bỏ sót DependencyInjectionTestExecutionListenerkhi thêm tùy chỉnh TestExecutionListeners. Nếu bạn làm vậy, tất cả các autowires sẽ là null.


1 Kỹ thuật này giải quyết vấn đề của tôi khi muốn sử dụng DbUnit và chỉ tải dữ liệu một lần mỗi lớp
Brad

+1 Điều này là hoàn hảo ... cho những người không bị ràng buộc vào phiên bản cổ xưa của Spring. :(
Mike Miller

1
Điều này sẽ beforeTestClass()được gọi trước hay sau khi khởi tạo ngữ cảnh?
Bữa tối

@Dims sau khi ngữ cảnh được khởi tạo
Anand Rockzz

7

Dễ dàng sử dụng @BeforeAllMethods/ @AfterAllMethodschú thích để chạy một phương thức bên trong ngữ cảnh cá thể (không tĩnh), nơi tất cả các giá trị được đưa vào sẽ có sẵn.

Có một thư viện thử nghiệm đặc biệt cho điều này:

https://mvnrepository.com/artifact/org.bitbucket.radistao.test/before- after-spring-test-runner/0.1.0

https://bitbucket.org/radistao/before- after-spring-test-runner/

Hạn chế duy nhất: chỉ hoạt động cho thử nghiệm mùa xuân .

(Tôi là nhà phát triển của thư viện thử nghiệm này)


0

Tôi chưa bao giờ thử nhưng có thể bạn có thể tạo một hàm tạo không đối số và gọi cho bạn hàm từ đó?


Điều này sẽ hoạt động, vấn đề là tôi cần khả năng ghi đè hành động này trong các lớp mở rộng lớp thử nghiệm cơ sở này
Roman

@Roman: ồ, giờ thì ra rồi. Thêm điều này vào bài đăng của bạn, bình luận này làm cho mọi thứ rõ ràng hơn nhiều.
Roman

Khối mã lệnh sẽ được gọi bao nhiêu lần các trường hợp kiểm thử ở đó. Đối với mỗi phương pháp thử nghiệm, đối tượng lớp Thử nghiệm mới sẽ được tạo. Vì vậy, sử dụng constructor không phải là một giải pháp ở đây
manikanta

Ngoài ra, điều này sẽ không hoạt động với việc tiêm phụ thuộc dựa vào đối tượng đã được xây dựng.
Mike Miller

0

Bài báo thảo luận về 2 giải pháp rất hay cho vấn đề này:

  1. "sạch" junit với Runner tùy chỉnh (sử dụng giao diện nhưng bạn có thể mở rộng nó với chú thích tùy chỉnh, ví dụ: @BeforeInstance)
  2. Các thính giả thực hiện Spring như Espen đã đề cập trước đây.

0

CẬP NHẬT: Vui lòng xem comment của Cherry để biết tại sao gợi ý bên dưới có sai sót. (Tôi giữ câu trả lời ở đây thay vì xóa vì nhận xét có thể cung cấp thông tin hữu ích cho người khác về lý do tại sao điều này không hoạt động.)


Một lựa chọn khác đáng xem xét nếu sử dụng phương pháp tiêm phụ thuộc (ví dụ: Spring) là @PostConstruct. Điều này sẽ đảm bảo việc tiêm phụ thuộc đã hoàn tất, điều này sẽ không xảy ra trong một hàm tạo:

@PostConstruct
public void init() {
    // One-time initialization...
}


7
Giải pháp rất tồi trong trường hợp kiểm tra Junit. Junit tạo cá thể lớp thử nghiệm mọi lúc khi nó chạy một phương thức thử nghiệm. Vì vậy, nếu có 6 phương thức kiểm tra trong lớp, một hàm tạo lớp @Before@Aftercác phương thức sẽ được gọi 6 lần! Vì vậy, trong ngữ cảnh này @PostConstructhoạt động giống như @Beforechú thích. Bạn có thể kiểm tra nó một cách đơn giản: chỉ cần đặt 2 phương pháp kiểm tra trong lớp kiểm tra, thêm @PostConstruct public void init() {System.out.println("started");}và xem trong nhật ký bao nhiêu lần nó được in.
Cherry

Để biết thông tin, tôi vừa xem qua tài liệu JUnit xác nhận những gì được mô tả trong nhận xét ở trên về việc JUnit tạo một phiên bản cho mỗi lần @Testchạy: "Để chạy phương thức, JUnit đầu tiên tạo một phiên bản mới của lớp sau đó gọi phương thức chú thích."
Steve Chambers

-2

Chỉ cần sử dụng @BeforeClass:

@BeforeClass
public static void init() {
}

Không có nghĩa initlà không tĩnh vì mỗi bài kiểm tra được chạy trong một phiên bản riêng biệt. Phiên bản initđang chạy sẽ không khớp với phiên bản của bất kỳ bài kiểm tra nào.

Lý do duy nhất mà bạn có thể muốn nó không phải là static là ghi đè nó trong các lớp con, nhưng bạn cũng có thể làm điều này với các phương thức static. Chỉ cần sử dụng cùng một tên, và chỉ initphương thức lớp con sẽ được gọi.


2
Toàn bộ câu hỏi này là về khả năng thực hiện nó theo cách không tĩnh, điều này cần thiết nếu bạn cần một số biến cá thể trên lớp.
Simon Forsberg

@SimonForsberg Có, và tôi đang nói rằng câu hỏi là một vấn đề XY. Nhóm nghiên cứu cho biết vấn đề đang đè nặng lên hành vi ở các lớp trẻ. Nếu ví dụ cần các biến cá thể, thì tôi có thể đề xuất một cái gì đó khác.
fgb


@SimonForsberg Đó là nhận xét mà tôi đang nói đến. Còn nó thì sao?
fgb
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.