Comme le titre l'indique, je recherche un moyen simple d'exécuter automatiquement des tests JUnit 4.x à l'aide d'Eclipse.
Un exemple serait d'exécuter le même test 10 fois de suite et de rapporter le résultat.
Nous avons déjà un moyen complexe de le faire, mais je cherche un moyen simple de le faire afin d’être sûr que le test flaky que j’essayais de corriger reste fixe.
Une solution idéale serait un plugin/paramètre/fonctionnalité Eclipse dont je ne suis pas au courant.
La manière la plus simple (comme dans le moins de nouveau code requis) de procéder consiste à exécuter le test en tant que test paramétré (annotez avec un @RunWith(Parameterized.class)
et ajoutez une méthode pour fournir 10 paramètres vides). De cette façon, le framework exécutera le test 10 fois.
Ce test doit être le seul test de la classe ou, mieux, toutes les méthodes de test doivent être exécutées 10 fois dans la classe.
Voici un exemple:
@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");
}
}
Avec ce qui précède, il est même possible de le faire avec un constructeur sans paramètre, mais je ne suis pas sûr si les auteurs du framework l'ont prévu, ou si cela se cassera dans le futur.
Si vous implémentez votre propre coureur, le coureur peut alors exécuter le test 10 fois. Si vous utilisez un coureur tiers, vous pouvez utiliser la nouvelle annotation @Rule
avec la version 4.7 et implémenter l'interface MethodRule
de sorte qu'elle prenne l'instruction et l'exécute 10 fois dans une boucle for. L'inconvénient actuel de cette approche est que @Before
et @After
ne sont exécutés qu'une seule fois. Cela changera probablement dans la prochaine version de JUnit (le @Before
sera exécuté après le @Rule
), mais vous agirez de la même manière sur la même instance de l'objet (ce qui n'est pas le cas du lanceur Parameterized
). Cela suppose que le coureur avec lequel vous exécutez la classe reconnaît correctement les annotations @Rule
. Ce n'est le cas que s'il délègue aux coureurs JUnit.
Si vous exécutez avec un programme personnalisé qui ne reconnaît pas l'annotation @Rule
, vous devez alors écrire votre propre programme qui délègue de manière appropriée à ce programme et l'exécute 10 fois.
Notez qu'il existe d'autres moyens de résoudre ce problème (comme le coureur Theories), mais ils nécessitent tous un coureur. Malheureusement, JUnit ne prend actuellement pas en charge les couches de coureurs. C'est un coureur qui enchaîne les autres coureurs.
J'ai trouvé que l'annotation de répétition de Spring est utile pour ce genre de chose:
@Repeat(value = 10)
Dernière documentation (Spring Framework 4.3.11.RELEASE API):
Avec IntelliJ, vous pouvez le faire à partir de la configuration de test. Une fois que vous avez ouvert cette fenêtre, vous pouvez choisir d’exécuter le test autant de fois que vous le souhaitez.
lorsque vous exécuterez le test, intellij exécutera tous les tests que vous avez sélectionnés pour le nombre de fois que vous avez spécifié.
Inspiré sur cette solution :
Utilisez l'annotation @Repeat
comme ceci:
public class MyTestClass {
@Rule
public RepeatRule repeatRule = new RepeatRule();
@Test
@Repeat(10)
public void testMyCode() {
//your test code goes here
}
}
Vous aurez seulement besoin de ces deux classes:
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;
}
}
2016-10-25 Edit: Pour utiliser cette solution lors de l'utilisation de @RunWith(PowerMockRunner.class)
, effectuez une mise à jour vers Powermock 1.6.5 (qui inclut ce correctif). .
Avec JUnit 5, j'ai pu résoudre ce problème en utilisant l'annotation @RepeatedTest :
@RepeatedTest(10)
public void testMyCode() {
//your test code goes here
}
Notez que l'annotation @Test
ne doit pas être utilisée avec @RepeatedTest
.
Quelque chose ne va pas avec:
@Test
void itWorks() {
// stuff
}
@Test
void itWorksRepeatably() {
for (int i = 0; i < 10; i++) {
itWorks();
}
}
Contrairement au cas où vous testez chacune des valeurs d'un tableau, vous ne vous souciez pas de savoir si l'exécution a échoué.
Nul besoin de faire en configuration ou en annotation ce que vous pouvez faire en code.
Il existe une annotation intermittente dans la bibliothèque tempus-fugit qui fonctionne avec @Rule
de JUnit 4.7 pour répéter un test plusieurs fois ou avec @RunWith
.
Par exemple,
@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {
private static int testCounter = 0;
@Test
@Intermittent(repition = 99)
public void annotatedTest() {
testCounter++;
}
}
Une fois le test exécuté (avec IntermittentTestRunner dans le @RunWith
), testCounter
serait égal à 99.
Cela fonctionne beaucoup plus facilement pour moi.
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;
}
}
Je construis un module qui permet de faire ce genre de test. Mais il est concentré non seulement dans la répétition. Mais en garantissant qu'un morceau de code est Thread safe.
https://github.com/anderson-marques/concurrent-testing
Dépendance Maven:
<dependency>
<groupId>org.lite</groupId>
<artifactId>concurrent-testing</artifactId>
<version>1.0.0</version>
</dependency>
Exemple d'utilisation:
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");
}
}
Ceci est un projet open source. N'hésitez pas à vous améliorer.
Vous pouvez exécuter votre test JUnit à partir d'une méthode principale et le répéter autant de fois qu'il vous faut:
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
}
}
}
}
C'est essentiellement la réponse fournie par Yishai ci-dessus, réécrite en 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() { }
}