Cara mudah menjalankan tes junit yang sama berulang kali?

121

Seperti judulnya, saya mencari cara sederhana untuk menjalankan pengujian JUnit 4.x beberapa kali berturut-turut secara otomatis menggunakan Eclipse.

Contoh akan menjalankan pengujian yang sama 10 kali berturut-turut dan melaporkan kembali hasilnya.

Kami sudah memiliki cara yang rumit untuk melakukan ini, tetapi saya sedang mencari cara sederhana untuk melakukannya sehingga saya dapat yakin bahwa tes tak stabil yang saya coba perbaiki tetap diperbaiki.

Solusi ideal adalah plugin / pengaturan / fitur Eclipse yang tidak saya sadari.

Stefan Thyberg
sumber
5
Saya sangat ingin tahu mengapa Anda ingin melakukan ini.
Buhb
Saya menjalankan uji kotak hitam besar, telah membuat perubahan kecil dan ingin melihat bagaimana hal itu memengaruhi stabilitas uji bersisik sebelumnya.
Stefan Thyberg
Memang, kecuali Anda ingin menjalankannya hingga gagal, sementara saya hanya ingin menjalankannya beberapa kali, yang mungkin memengaruhi jawaban yang saya dapatkan.
Stefan Thyberg
4
Apakah Anda menentang TestNG karena jika tidak maka Anda bisa menggunakan @Test (invocationCount = 10) dan hanya itu saja.
Robert Massaioli
1
Saya tidak "menentang" TestNG, kami hanya tidak menggunakannya dalam proyek itu.
Stefan Thyberg

Jawaban:

123

Cara termudah (dengan jumlah paling sedikit kode baru yang diperlukan) untuk melakukan ini adalah dengan menjalankan pengujian sebagai pengujian parametrized (beri anotasi dengan @RunWith(Parameterized.class)dan tambahkan metode untuk menyediakan 10 parameter kosong). Dengan begitu framework akan menjalankan pengujian 10 kali.

Pengujian ini harus menjadi satu-satunya pengujian di kelas, atau lebih baik letakkan semua metode pengujian yang harus dijalankan 10 kali di kelas.

Berikut ini contohnya:

@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");
    }
}

Dengan hal di atas, bahkan mungkin untuk melakukannya dengan konstruktor tanpa parameter, tetapi saya tidak yakin apakah penulis kerangka bermaksud itu, atau apakah itu akan rusak di masa mendatang.

Jika Anda menerapkan pelari Anda sendiri, Anda dapat meminta pelari menjalankan pengujian 10 kali. Jika Anda menggunakan runner pihak ketiga, maka dengan 4.7, Anda dapat menggunakan @Ruleanotasi baru dan mengimplementasikan MethodRuleantarmukanya sehingga ia mengambil pernyataan dan mengeksekusinya 10 kali dalam perulangan for. Kerugian saat ini dari pendekatan ini adalah @Beforedan @Afterdijalankan hanya sekali. Ini kemungkinan akan berubah di versi JUnit berikutnya (yang @Beforeakan berjalan setelah @Rule), tetapi terlepas dari apa pun Anda akan bertindak pada contoh objek yang sama (sesuatu yang tidak benar dari Parameterizedpelari). Ini mengasumsikan bahwa pelari apa pun yang Anda jalankan dengan kelas mengenali @Ruleanotasi dengan benar . Itu hanya kasus jika itu mendelegasikan ke pelari JUnit.

Jika Anda menjalankan dengan runner kustom yang tidak mengenali @Ruleanotasi, Anda benar-benar harus menulis runner Anda sendiri yang mendelegasikan secara tepat ke Runner tersebut dan menjalankannya 10 kali.

Perhatikan bahwa ada cara lain untuk menyelesaikan masalah ini (seperti pelari Teori) tetapi semuanya membutuhkan pelari. Sayangnya JUnit saat ini tidak mendukung lapisan pelari. Itu adalah pelari yang mengikat pelari lainnya.

Yishai
sumber
2
Sayangnya saya sudah menjalankan @RunWith dengan pelari lain, tetapi jika tidak, ini akan menjadi solusi yang ideal.
Stefan Thyberg
Ya, ini adalah solusi yang saya ingin miliki dan yang terbaik bagi kebanyakan orang jadi saya akan terus maju dan menerima jawabannya.
Stefan Thyberg
Untuk solusi alternatif dan mungkin lebih sedikit hacky lihat: stackoverflow.com/a/21349010/281545
Mr_and_Mrs_D
Solusi bagus! Saya mendapat pengecualian yang memberi tahu saya bahwa metode data harus mengembalikan Iterable of Array. Saya memperbaikinya sesuai: @ Parameterized.Parameters public static Iterable <Object []> data () {return Arrays.asList (new Object [20] [0]); }
nadre
1
Bisakah Anda menautkan ke jawaban ini untuk JUnit 5? Ini menjelaskan fitur yang diminta yang ditambahkan di JUnit 5
Marcono1234
100

Dengan IntelliJ, Anda dapat melakukan ini dari konfigurasi pengujian. Setelah Anda membuka jendela ini, Anda dapat memilih untuk menjalankan pengujian beberapa kali.

masukkan deskripsi gambar di sini

ketika Anda menjalankan pengujian, intellij akan menjalankan semua pengujian yang telah Anda pilih untuk berapa kali Anda tentukan.

Contoh menjalankan 624 pengujian 10 kali: masukkan deskripsi gambar di sini

smac89
sumber
4
Itu sempurna, sekarang jika Anda dapat menunjukkan cara gerhana untuk melakukan ini, itu akan menjawab pertanyaan OP langsung
khal
Mengandalkan alat khusus untuk menghosting logika atau persyaratan aktual adalah anti-pola.
Mickael
1
@Mickael Mengulangi tes N kali biasanya bukan persyaratan pengujian. Padahal tes harus bersifat deterministik, sehingga tidak peduli berapa kali diulang, selalu akan menghasilkan hasil yang sama. Bisakah Anda menjelaskan pola anti yang Anda bicarakan?
smac89
Jika mengulangi pengujian bermanfaat untuk 1 pengembang, kemungkinan akan berguna bagi orang lain. Jadi, jika runtime pengujian dan kode dapat menghosting logika untuk mengaktifkan pengulangan, itu harus lebih disukai karena memungkinkan untuk memfaktorkan upaya dan solusi dan memungkinkan kontributor untuk menggunakan alat yang diinginkan dengan hasil yang sama. Menempatkan logika yang dapat digunakan kembali di IDE / area pengembang ketika dapat dimasukkan ke dalam kode adalah jenis faktorisasi yang hilang.
Mickael
68

Saya telah menemukan bahwa anotasi berulang Spring berguna untuk hal semacam itu:

@Repeat(value = 10)

Dokumen terbaru (Spring Framework 4.3.11.RELEASE API):

laura
sumber
46
Mengubah kerangka pengujian bukanlah yang saya sebut sebagai cara mudah untuk melakukannya.
Stefan Thyberg
3
Anda tidak perlu mengubah framework pengujian Anda - ini berfungsi dengan baik dengan JUnit. Kelemahan utamanya adalah JUnit masih melihatnya sebagai pengujian tunggal. Jadi saat pertama kali rusak, eksekusi akan berhenti. Namun, jika Anda belum menggunakan Spring, mungkin ini bukan cara yang Anda inginkan ...
tveon
Sepertinya tidak berhasil untuk saya (Spring 4.3.6 melalui Spring Boot 1.5.1)
David Tonhofer
Tidak bekerja untuk saya dengan sepatu bot musim semi 2.1.6 dan junit 5
jo-
Bekerja sempurna dengan spring boot 2. Jangan lupa untuk menambahkan @RunWith (SpringRunner :: class), sesuai dengan link 'unit testing in spring' di poster!
Agoston Horvath
33

Terinspirasi oleh sumber daya berikut:

Contoh

Buat dan gunakan @Repeatanotasi sebagai berikut:

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

Menggunakan solusi ini dengan @RunWith(PowerMockRunner.class), memerlukan pembaruan ke Powermock 1.6.5 (yang menyertakan tambalan ).

R. Oosterholt
sumber
Iya. Bagaimana Anda menjalankan tes?
R. Oosterholt
Saya tidak menggunakan gerhana sendiri. Mungkin Anda tidak menggunakan pelari uji junut 4? (lihat dokumen "Menyesuaikan Konfigurasi Tes" )
R. Oosterholt
29

Dengan JUnit 5 saya dapat menyelesaikan ini menggunakan anotasi @RepruptedTest :

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

Perhatikan bahwa @Testanotasi tidak boleh digunakan bersama @RepeatedTest.

César Alberca
sumber
Kedengarannya sangat menjanjikan, hanya untuk dicatat belum ada versi rilisnya.
9ilsdx 9rvj 0lo
1
JUnit 5 mencapai GA beberapa minggu lalu.
mkobit
11

Ada yang salah dengan:

@Test
void itWorks() {
    // stuff
}

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

Tidak seperti kasus saat Anda menguji setiap larik nilai, Anda tidak terlalu peduli menjalankan mana yang gagal.

Tidak perlu melakukan konfigurasi atau penjelasan apa yang dapat Anda lakukan dalam kode.

soru
sumber
2
Saya ingin menjalankan beberapa pengujian sebagai pengujian unit normal dan mendapatkan pelacakan serta status untuk masing-masing pengujian.
Stefan Thyberg
24
Dalam hal ini "@Before" s dan "@After" tidak akan dijalankan
Bogdan
3
Ini bersama dengan memanggil @Beforemetode beranotasi secara manual sebelum itWorks() memecahkan masalah saya.
João Neves
Tahukah Anda konsep KERING? en.wikipedia.org/wiki/Don%27t_repeat_yourself Saya sarankan untuk membuat beberapa pengaturan daripada menyalin menempelkan loop Anda di mana-mana.
Kikiwa
Antrian edit untuk jawaban ini sudah penuh; oleh karena itu, saya akan menaruhnya di komentar: untuk JUnit4, pengujian harus bersifat publik.
Richard Jessop
7

Ini bekerja lebih mudah bagi saya.

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;
    }
}
Qualk
sumber
Luar biasa karena tidak menggunakan kerangka kerja lain dan benar-benar berfungsi dengan JUnit 3 (penting untuk android)
Vladimir Ivanov
1
Implementasi dengan JUnit4 dapat dilakukan dengan 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); } } }Meskipun setidaknya di plugin Eclipse JUnit Anda mendapatkan hasil seperti: "10/1 tes lulus"
Peter Wippermann
7

Ada anotasi Intermiten di perpustakaan tempus-fugit yang bekerja dengan JUnit 4.7 @Ruleuntuk mengulangi pengujian beberapa kali atau bersama @RunWith.

Sebagai contoh,

@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {

   private static int testCounter = 0;

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

Setelah pengujian dijalankan (dengan IntermittentTestRunner di @RunWith), testCounterakan sama dengan 99.

Toby
sumber
Ya, itu masalah yang sama di sini, sudah menggunakan pelari lain sehingga tidak bisa menggunakan yang ini, ide bagus.
Stefan Thyberg
Ya, saya mengalami masalah yang sama dengan RunWith ... seiring berjalannya waktu saya mengubah tempus-fugit untuk mengatasinya sedikit, Anda dapat menggunakan @Rule daripada runner saat Anda ingin menjalankan berulang kali. Anda menandainya dengan @Repeating, bukan intermiten. Versi aturan tidak akan berjalan @ Before / @ Afters sekalipun. Lihat tempus-fugit.googlecode.com/svn/site/documentation/… (gulir ke bawah untuk memuat / merendam pengujian) untuk lebih jelasnya.
Toby
0

Saya membangun modul yang memungkinkan melakukan tes semacam ini. Tapi itu difokuskan tidak hanya pada pengulangan. Tetapi untuk menjamin bahwa beberapa bagian kode aman untuk Thread.

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

Ketergantungan Maven:

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

Contoh penggunaan:

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");
    }
}

Ini adalah proyek open source. Silakan perbaiki.

Anderson Marques
sumber
0

Anda dapat menjalankan pengujian JUnit dari metode utama dan mengulanginya berkali-kali yang Anda butuhkan:

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

            }
        }

    }

}
silver_mx
sumber
0

Ini pada dasarnya adalah jawaban yang diberikan Yishai di atas, ditulis ulang di 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() { }
}
menandai
sumber