27

I have the classical structure for tests, I have a test suite of different suites like DatabaseTests, UnitTests etc. Sometimes those suites contains other suites like SlowDatabaseTests, FastDatabaseTests etc.

What I want is to randomize the running order of tests so I will make sure they are not dependent to each other. Randomization should be at every level, like suite should shuffle test class order, and test class should shuffle test method order.

If it is possible to do this in Eclipse that will be the best.

nimcap
  • 10,062
  • 15
  • 61
  • 69
  • 8
    Randomizing the order wouldn't prove anything, since the random might work "by accident". Also, it would not be repeatable, and so you'd never be able to track down the cause of a failure. – skaffman Sep 18 '09 at 12:45
  • 3
    If I write my execution order to somewhere everytime tests are run, I will be able to track the cause. – nimcap Sep 18 '09 at 14:02
  • 10
    You could make it repeatable by giving Random() a seed that is a function of the date without the time. If you do this, I strongly recommend a continuous build so you notice when the tests fail. – NamshubWriter Sep 29 '09 at 15:25
  • 7
    @skaffman That may be true, but if you don't randomise the order, the bug will still be there, but you'll never find out about it. So it depends what you prefer: (a) a bug which you don't know about, or (b) a bug which you know about but can't track down. – Hakanai Sep 12 '13 at 01:12
  • 1
    Mannn this sounds like a terrible idea to me. Repeatability is crucial to finding/resolving bugs. If you want to try varied order permutations, code it up to do so explicitly. Tracking down issues with non-deterministic "unit" tests is one of the biggest sources of wasted time for me. Fortunately in this case, at least the logs are likely to tell you which order the tests ran in, so you could explicitly repeat the situation if need be. Non-determinism + minimal logging = complete waste of time, but no logging is the ideal, along with complete determinism is the ideal. – Ryan Nov 08 '13 at 21:28
  • 5
    @Ryan: The tests can output the RNG seed at the beginning of the tests so it can be repeated. – nimcap Nov 09 '13 at 18:43

7 Answers7

18

You do have a Sortable but I can't see how you would use it.

You could extend BlockJUnit4ClassRunner and have computeTestMethods() return a randomized copy of super.computeTestMethods(). Then use the @RunWith to set that as the runner to use.

e.g.

package com.stackoverflow.mlk;

import java.util.Collections;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;

public class RandomBlockJUnit4ClassRunner extends BlockJUnit4ClassRunner {

    public RandomBlockJUnit4ClassRunner(Class<?> klass)
            throws InitializationError {
        super(klass);
    }

    protected java.util.List<org.junit.runners.model.FrameworkMethod> computeTestMethods() {
        java.util.List<org.junit.runners.model.FrameworkMethod> methods = super.computeTestMethods();
        Collections.shuffle(methods);
        return methods;
    }

}

Then

@RunWith(com.stackoverflow.mlk.RandomBlockJUnit4ClassRunner.class)
public class RandomOrder {
    @Test
    public void one() {
    }

    @Test
    public void two() {
    }

    @Test
    public void three() {
    }
}
y.luis.rojo
  • 1,794
  • 4
  • 22
  • 41
Michael Lloyd Lee mlk
  • 14,561
  • 3
  • 44
  • 81
  • 1
    good answer but not quite sufficient I need to implement a Suite runner too, to randomize test classes order. Besides I have lots of tests and I don't want to put @RunWith annotation into all of them. I think that can be handled in Suite runner – nimcap Oct 05 '09 at 10:58
  • 2
    I found this very useful, and I created a small Java project that randomize tests and suites. For more information, visit its page: http://randomjunit.sourceforge.net/ – AngocA Jun 24 '11 at 06:32
4

https://github.com/KentBeck/junit/pull/386 introduces some orders but not RANDOM. Probably you do not really want this; tests should run deterministically. If you need to verify that different permutations of tests still pass, either test all permutations; or, if this would be impractically slow, introduce a “random” seed for shuffling that is determined by an environment variable or the like, so that you can reproduce any failures. http://hg.netbeans.org/main/file/66d9fb12e98f/nbjunit/src/org/netbeans/junit/MethodOrder.java gives an example of doing this for JUnit 3.

Jesse Glick
  • 24,539
  • 10
  • 90
  • 112
3

In general what you need to do is to write your own test runner and in the test runner class aggregate the methods and randomly run each test (make sure you don't run a test twice).

Read more about the test framework and how to write your own test runner here: http://www.ddj.com/architect/184415674

ganoro
  • 508
  • 5
  • 11
  • Note that the ddj.com article describes the JUnit3 test runner. If you try this, be aware that if your tests use TestSetup or try to do suite-level setup and tear down by extending TestSuite, the approach suggested here won't work; the test runner doesn't "see" TestSuites or TestDecorators. Writing your own JUnit3 test runner also won't work if you run tests from an IDE like Eclipse. – NamshubWriter Sep 29 '09 at 15:35
1

In JUnit 4.13, to run the tests within a test class in random order, write a small helper class:

import org.junit.runner.manipulation.Ordering;

import java.util.Random;

public class RandomOrder implements Ordering.Factory {
    @Override
    public Ordering create(Ordering.Context context) {
        long seed = new Random().nextLong();
        System.out.println("RandomOrder: seed = " + seed);
        return Ordering.shuffledBy(new Random(seed));
    }
}

Then, annotate your test class with:

@OrderWith(RandomOrder.class)

This way, the test methods of this one class are run in random order. Plus, if they unexpectedly fail, you know the random seed to repeat exactly this order.

I don't know though how to configure this for a whole project or a test suite.

0

I will make sure they are not dependent to each other

You should make sure that this is the case without relying on random execution order. What makes you fear that dependencies may exist?

  • May I ask why people downvote my answer? What is wrong with it? –  Sep 18 '09 at 12:45
  • 6
    We are being very careful while writing unit tests and making them independent. But we are also writing production code very careful, bugs happen to appear, there is no guarantee that they will be independent. PS. We are using JUnit for not only unit tests but for functional tests too, so they sometimes leave the DB at some state. – nimcap Sep 18 '09 at 14:05
  • 6
    @lutz I guess because you countered with another question without answering OP's question – akuhn Sep 19 '09 at 21:27
  • 3
    "Make sure that this is the case"? How would you make sure that this is the case if you're always running the tests in order? – Hakanai Sep 12 '13 at 01:16
  • 1
    @nimcap a test database should be reset at the end of each test, either by using a transaction for each test or by purging and reinitialising it. Anyways, JUnit is probably not the right tool for integration tests. – MauganRa Nov 28 '16 at 11:09
  • 1
    JUnit is fine for integration tests imho. Though ideally they'd be in a separate package or folder so it's clear that they require a live backend and aren't isolated unit tests. – Vivek Chavda May 03 '17 at 20:15
0

This issue is open on JUnit GitHub since 2 years, and point out 2 independent issues:
- Tests depending on the execution order;
- Non repeatable tests.

Consider adressing the issue at the root, rather than trying to use the framework to do the job afterwards. Use setUp and tearDown method to guarantee isolation, and test at the smallest level.

0

Here is a solution with Gradle and JUnit 5.8.0

Step 1 : Ensure that you have latest JUnit version dependency.
Step 2 : Define the required properties under build.gradle test section

test {  
    useJUnitPlatform()
    systemProperties([
         //Random in method level
        'junit.jupiter.testmethod.order.default': 'org.junit.jupiter.api.MethodOrderer$Random', 
         // Random in class level
        'junit.jupiter.testclass.order.default' : 'org.junit.jupiter.api.ClassOrderer$Random',
        // Log configuration to see the seed
        'java.util.logging.config.file' : file('src/test/resources/logging.properties')
    ])
    //To print the JUnit logs in console
    testLogging {
        events "passed", "skipped", "failed", "standardOut", "standardError"
    }
}

Step 3: Define logging.properties under src/test/resources

.level=CONFIG
java.util.logging.ConsoleHandler.level=CONFIG
org.junit.jupiter.api.ClassOrderer$Random.handlers=java.util.logging.ConsoleHandler
org.junit.jupiter.api.MethodOrderer$Random.handlers=java.util.logging.ConsoleHandler

Step 4 : Run test. gradlew clean test

You can see the seed used for the random test in the console

CONFIG: ClassOrderer.Random default seed: 65423695211256721
CONFIG: MethodOrderer.Random default seed: 6542369521653287

In case of flaky test, you can reproduce it by configuring the same seed where the JUnit tests were failing

systemProperties([
'junit.jupiter.execution.class.order.random.seed' : '65423695211256721'
'junit.jupiter.execution.order.random.seed'       : '6542369521653287'
])

References : how-to-randomize-tests-in-junit , Random

jfk
  • 4,335
  • 34
  • 27