Cách dễ dàng để chạy đi chạy lại cùng một bài kiểm tra junit?


121

Giống như tiêu đề đã nói, tôi đang tìm một số cách đơn giản để chạy các bài kiểm tra JUnit 4.x nhiều lần liên tiếp bằng Eclipse.

Một ví dụ sẽ chạy cùng một bài kiểm tra 10 lần liên tiếp và báo cáo lại kết quả.

Chúng tôi đã có một cách phức tạp để thực hiện việc này nhưng tôi đang tìm một cách đơn giản để thực hiện nó để tôi có thể chắc chắn rằng bài kiểm tra bị bong tróc mà tôi đang cố gắng khắc phục vẫn được khắc phục.

Một giải pháp lý tưởng sẽ là một plugin / cài đặt / tính năng Eclipse mà tôi không biết.


5
Tôi rất tò mò về lý do tại sao bạn muốn làm điều này.
Buhb 29/09/09

Tôi đang chạy thử nghiệm hộp đen lớn, đã thực hiện một thay đổi nhỏ và muốn xem điều đó ảnh hưởng như thế nào đến sự ổn định của thử nghiệm bị bong tróc trước đây này.
Stefan Thyberg 29/09/09

Thực sự là vậy, ngoại trừ việc bạn muốn nó chạy cho đến khi thất bại, trong khi tôi chỉ muốn chạy nó một số lần, điều này có thể ảnh hưởng đến câu trả lời mà tôi nhận được.
Stefan Thyberg

4
Bạn có chống lại TestNG không vì nếu không thì bạn chỉ có thể sử dụng @Test (invocationCount = 10) và đó là tất cả những gì cần có.
Robert Massaioli

1
Tôi không "chống lại" TestNG, chúng tôi chỉ không sử dụng nó trong dự án đó.
Stefan Thyberg

Câu trả lời:


123

Cách dễ nhất (với số lượng ít nhất là yêu cầu mã mới) để làm điều này là chạy thử nghiệm dưới dạng thử nghiệm tham số hóa (chú thích bằng một @RunWith(Parameterized.class)và thêm một phương thức để cung cấp 10 tham số trống). Bằng cách đó, khuôn khổ sẽ chạy thử nghiệm 10 lần.

Bài kiểm tra này cần phải là bài kiểm tra duy nhất trong lớp, hay tốt hơn là tất cả các phương pháp kiểm tra cần được chạy 10 lần trong lớp.

Đây là một ví dụ:

@RunWith(Parameterized.class)
public class RunTenTimes {

    @Parameterized.Parameters
    public static Object[][] data() {
        return new Object[10][0];
    }

    public RunTenTimes() {
    }

    @Test
    public void runsTenTimes() {
        System.out.println("run");
    }
}

Với những điều trên, bạn thậm chí có thể làm điều đó với một hàm tạo ít tham số, nhưng tôi không chắc liệu các tác giả khung có ý định như vậy hay không, hoặc liệu điều đó có bị phá vỡ trong tương lai hay không.

Nếu bạn đang thực hiện người chạy của riêng mình, thì bạn có thể để người chạy thử nghiệm 10 lần. Nếu bạn đang sử dụng trình chạy bên thứ ba, thì với 4.7, bạn có thể sử dụng @Rulechú thích mới và triển khai MethodRulegiao diện để nó nhận câu lệnh và thực thi nó 10 lần trong một vòng lặp for. Nhược điểm hiện tại của phương pháp này là @Beforevà chỉ @Afterđược chạy một lần. Điều này có thể sẽ thay đổi trong phiên bản tiếp theo của JUnit ( @Beforesẽ chạy sau @Rule), nhưng bất kể bạn sẽ hành động trên cùng một phiên bản của đối tượng (điều gì đó không đúng với trình Parameterizedchạy). Điều này giả định rằng bất kỳ người chạy nào bạn đang chạy lớp với nó đều nhận ra chính xác các @Rulechú thích. Đó chỉ là trường hợp nếu nó được ủy quyền cho những người chạy JUnit.

Nếu bạn đang chạy với một trình chạy tùy chỉnh không nhận ra @Rulechú thích, thì bạn thực sự gặp khó khăn với việc phải viết trình chạy của riêng mình ủy quyền thích hợp cho Trình chạy đó và chạy nó 10 lần.

Lưu ý rằng có những cách khác để giải quyết vấn đề này (chẳng hạn như Trình chạy theo lý thuyết) nhưng tất cả đều yêu cầu người chạy. Rất tiếc, JUnit hiện không hỗ trợ các lớp người chạy. Đó là một vận động viên chạy dây chuyền các vận động viên khác.


2
Rất tiếc, tôi đã chạy @RunWith với một người chạy khác, nhưng nếu không thì đây sẽ là một giải pháp lý tưởng.
Stefan Thyberg 30/09/09

Vâng, đây là giải pháp mà tôi muốn có và sẽ tốt nhất cho hầu hết mọi người nên tôi sẽ tiếp tục và chấp nhận câu trả lời.
Stefan Thyberg

Để biết một giải pháp thay thế và có thể ít hack hơn, hãy xem: stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D

Giải pháp tốt! Tôi có một ngoại lệ nói với tôi rằng phương thức dữ liệu phải trả về một mảng có thể lặp lại. Tôi đã sửa nó cho phù hợp: @ Parameterized.Parameters public static Iterable <Object []> data () {return Arrays.asList (new Object [20] [0]); }
nadre

1
Bạn có thể vui lòng liên kết đến câu trả lời này cho JUnit 5 không? Nó mô tả tính năng được yêu cầu đã được thêm vào trong JUnit 5
Marcono1234, 22/09/18

100

Với IntelliJ, bạn có thể thực hiện việc này từ cấu hình thử nghiệm. Khi bạn mở cửa sổ này, bạn có thể chọn chạy thử nghiệm bất kỳ số lần nào bạn muốn ,.

nhập mô tả hình ảnh ở đây

khi bạn chạy thử nghiệm, intellij sẽ thực hiện tất cả các thử nghiệm bạn đã chọn cho số lần bạn chỉ định.

Ví dụ chạy 624 bài kiểm tra 10 lần: nhập mô tả hình ảnh ở đây


4
Đó là hoàn hảo, hiện nay nếu bạn có thể trỏ đến một cách nhật thực để làm điều này, điều đó sẽ trả lời câu hỏi của OP đến điểm
Khal

Dựa vào một công cụ cụ thể để lưu trữ các yêu cầu hoặc logic thực tế là một điều không phù hợp.
Mickael

1
@Mickael Việc lặp lại một thử nghiệm N lần thường không phải là yêu cầu của thử nghiệm. Trên thực tế, các phép thử phải mang tính xác định, để cho dù nó được lặp lại bao nhiêu lần, nó luôn tạo ra cùng một kết quả. Bạn có thể giải thích mô hình chống mà bạn nói đến?
smac89

Nếu lặp lại một bài kiểm tra hữu ích cho 1 nhà phát triển, nó có thể hữu ích cho những người khác. Vì vậy, nếu thời gian chạy thử nghiệm và mã có thể lưu trữ logic để cho phép lặp lại, nó nên được ưu tiên hơn vì nó cho phép phân tích nhân tố của nỗ lực và giải pháp và cho phép những người đóng góp sử dụng công cụ theo ý muốn với cùng một kết quả. Đặt logic có thể sử dụng lại trong khu vực IDE / nhà phát triển khi nó có thể được đưa vào mã là loại thiếu thừa số hóa.
Mickael

68

Tôi nhận thấy rằng chú thích lặp lại của Spring rất hữu ích cho những việc đó:

@Repeat(value = 10)

Tài liệu mới nhất (Spring Framework 4.3.11.RELEASE API):


46
Thay đổi khung thử nghiệm không phải là cách mà tôi gọi là một cách dễ dàng để thực hiện.
Stefan Thyberg 29/09/09

3
Bạn không cần phải thay đổi khung thử nghiệm của mình - nó hoạt động tốt với JUnit. Hạn chế chính là JUnit vẫn xem nó như một thử nghiệm đơn lẻ. Vì vậy, lần đầu tiên nó bị phá vỡ, việc thực thi sẽ dừng lại. Tuy nhiên, nếu bạn chưa sử dụng Spring, thì có lẽ đó không phải là cách bạn muốn ...
tveon

Không có vẻ làm việc cho tôi (Spring 4.3.6 qua mùa xuân Boot 1.5.1)
David Tonhofer

Không hoạt động với tôi với khởi động mùa xuân 2.1.6 và ngày 5 tháng 6
jo-

Hoạt động hoàn hảo với khởi động mùa xuân 2. Đừng quên thêm @RunWith (SpringRunner :: class), theo liên kết 'unit testing in spring' của người đăng!
Agoston Horvath

33

Lấy cảm hứng từ các nguồn sau:

Thí dụ

Tạo và sử dụng @Repeatchú thích như sau:

public class MyTestClass {

    @Rule
    public RepeatRule repeatRule = new RepeatRule();

    @Test
    @Repeat(10)
    public void testMyCode() {
        //your test code goes here
    }
}

Repeat.java

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention( RetentionPolicy.RUNTIME )
@Target({ METHOD, ANNOTATION_TYPE })
public @interface Repeat {
    int value() default 1;
}

RepeatRule.java

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class RepeatRule implements TestRule {

    private static class RepeatStatement extends Statement {
        private final Statement statement;
        private final int repeat;    

        public RepeatStatement(Statement statement, int repeat) {
            this.statement = statement;
            this.repeat = repeat;
        }

        @Override
        public void evaluate() throws Throwable {
            for (int i = 0; i < repeat; i++) {
                statement.evaluate();
            }
        }

    }

    @Override
    public Statement apply(Statement statement, Description description) {
        Statement result = statement;
        Repeat repeat = description.getAnnotation(Repeat.class);
        if (repeat != null) {
            int times = repeat.value();
            result = new RepeatStatement(statement, times);
        }
        return result;
    }
}

PowerMock

Sử dụng giải pháp này với @RunWith(PowerMockRunner.class), yêu cầu cập nhật lên Powermock 1.6.5 (bao gồm một bản vá ).


Đúng. Bạn đang thực hiện các bài kiểm tra như thế nào?
R. Oosterholt

Bản thân tôi không sử dụng nhật thực. Có thể bạn đang không sử dụng junut 4 test runner? (xem tài liệu "Tùy chỉnh cấu hình thử nghiệm" )
R. Oosterholt

29

Với JUnit 5, tôi có thể giải quyết vấn đề này bằng cách sử dụng chú thích @RepeatedTest :

@RepeatedTest(10)
public void testMyCode() {
    //your test code goes here
}

Lưu ý rằng @Testkhông nên sử dụng chú thích cùng với @RepeatedTest.


Nghe có vẻ rất hứa hẹn, chỉ cần lưu ý rằng vẫn chưa có phiên bản phát hành.
9ilsdx 9rvj 0lo

1
JUnit 5 đã đạt GA một vài tuần trước.
mkobit

11

Có gì sai với:

@Test
void itWorks() {
    // stuff
}

@Test
void itWorksRepeatably() {
    for (int i = 0; i < 10; i++) {
        itWorks();
    }
}

Không giống như trường hợp bạn đang kiểm tra từng mảng giá trị, bạn không đặc biệt quan tâm đến việc chạy không thành công.

Không cần phải làm trong cấu hình hoặc chú thích những gì bạn có thể làm trong mã.


2
Tôi muốn chạy một số bài kiểm tra như các bài kiểm tra đơn vị bình thường và lấy dấu vết và trạng thái cho mỗi bài kiểm tra.
Stefan Thyberg

24
Trong trường hợp này, "@Before" s và "@After" sẽ không được chạy
Bogdan

3
Điều này cùng với việc gọi @Beforephương thức được chú thích theo cách thủ công trước khi itWorks() giải quyết vấn đề của tôi.
João Neves

Bạn có biết khái niệm DRY? vi.wikipedia.org/wiki/Don%27t_repeat_yourself Tôi khuyên bạn nên thực hiện một số thiết lập thay vì sao chép dán vòng lặp của bạn ở khắp mọi nơi.
Kikiwa

Hàng đợi chỉnh sửa cho câu trả lời này đã đầy; do đó, tôi sẽ đưa ra một nhận xét: đối với JUnit4, các bài kiểm tra cần được công khai.
Richard Jessop

7

Điều này làm việc dễ dàng hơn nhiều đối với tôi.

public class RepeatTests extends TestCase {

    public static Test suite() {
        TestSuite suite = new TestSuite(RepeatTests.class.getName());

        for (int i = 0; i < 10; i++) {              
        suite.addTestSuite(YourTest.class);             
        }

        return suite;
    }
}

Tuyệt vời như không sử dụng khung công tác khác và thực sự hoạt động với JUnit 3 (rất quan trọng đối với Android)
Vladimir Ivanov

1
An thực hiện với Junit4 có thể được thực hiện với một Runner: public class RepeatRunner extends BlockJUnit4ClassRunner { public RepeatRunner(Class klass) throws InitializationError { super(klass); } @Override public void run(final RunNotifier notifier) { for (int i = 0; i < 10; i++) { super.run(notifier); } } }Mặc dù ít nhất trong Eclipse JUnit Plugin bạn có được kết quả như sau: "10/1 tests"
Peter Wippermann

7

Có một chú thích ngắt quãng trong thư viện tempus-fugit hoạt động với JUnit 4.7 @Ruleđể lặp lại một bài kiểm tra nhiều lần hoặc với @RunWith.

Ví dụ,

@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {

   private static int testCounter = 0;

   @Test
   @Intermittent(repition = 99)
   public void annotatedTest() {
      testCounter++;
   }
}

Sau khi chạy thử nghiệm (với IntermittentTestRunner trong @RunWith), testCountersẽ bằng 99.


Vâng, vấn đề tương tự ở đây, đã sử dụng một người chạy khác và vì vậy không thể sử dụng người chạy này, mặc dù vậy, ý kiến ​​hay.
Stefan Thyberg

Vâng, tôi đang gặp vấn đề tương tự với RunWith ... vì tôi đã điều chỉnh tempus-fugit để làm tròn nó một chút, bạn có thể sử dụng @Rule thay vì chạy khi bạn muốn chạy liên tục. Bạn đánh dấu nó bằng @Repeating thay vì ngắt quãng. Tuy nhiên, phiên bản quy tắc sẽ không chạy @ Before / @ Afters. Xem tempus-fugit.googlecode.com/svn/site/documentation/… (cuộn xuống thử nghiệm tải / ngâm) để biết thêm chi tiết.
Toby

0

Tôi xây dựng một mô-đun cho phép thực hiện loại thử nghiệm này. Nhưng nó không chỉ được tập trung lặp lại. Nhưng đảm bảo rằng một số đoạn mã là Thread an toàn.

https://github.com/anderson-marques/concurrent-testing

Sự phụ thuộc của Maven:

<dependency>
    <groupId>org.lite</groupId>
    <artifactId>concurrent-testing</artifactId>
    <version>1.0.0</version>
</dependency>

Ví dụ sử dụng:

package org.lite.concurrent.testing;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import ConcurrentTest;
import ConcurrentTestsRule;

/**
 * Concurrent tests examples
 */
public class ExampleTest {

    /**
     * Create a new TestRule that will be applied to all tests
     */
    @Rule
    public ConcurrentTestsRule ct = ConcurrentTestsRule.silentTests();

    /**
     * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
     */
    @Test
    @ConcurrentTest(requests = 20, threads = 10)
    public void testConcurrentExecutionSuccess(){
        Assert.assertTrue(true);
    }

    /**
     * Tests using 10 threads and make 20 requests. This means until 10 simultaneous requests.
     */
    @Test
    @ConcurrentTest(requests = 200, threads = 10, timeoutMillis = 100)
    public void testConcurrentExecutionSuccessWaitOnly100Millissecond(){
    }

    @Test(expected = RuntimeException.class)
    @ConcurrentTest(requests = 3)
    public void testConcurrentExecutionFail(){
        throw new RuntimeException("Fail");
    }
}

Đây là một dự án mã nguồn mở. Hãy tự do cải thiện.


0

Bạn có thể chạy thử nghiệm JUnit của mình từ một phương thức chính và lặp lại nó nhiều lần bạn cần:

package tests;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.Result;

public class RepeatedTest {

    @Test
    public void test() {
        fail("Not yet implemented");
    }

    public static void main(String args[]) {

        boolean runForever = true;

        while (runForever) {
            Result result = org.junit.runner.JUnitCore.runClasses(RepeatedTest.class);

            if (result.getFailureCount() > 0) {
                runForever = false;
               //Do something with the result object

            }
        }

    }

}

0

Đây thực chất là câu trả lời mà Yishai đã cung cấp ở trên, được viết lại bằng Kotlin:

@RunWith(Parameterized::class)
class MyTest {

    companion object {

        private const val numberOfTests = 200

        @JvmStatic
        @Parameterized.Parameters
        fun data(): Array<Array<Any?>> = Array(numberOfTests) { arrayOfNulls<Any?>(0) }
    }

    @Test
    fun testSomething() { }
}
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.