Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

SortMethodsWith allows the user to choose the order of execution of t…

…he methods within a test class.

The default order of execution of JUnit tests within a class
is deterministic but not predictable. Before 4.11, the
behaviour was to run the test methods in byte code order,
which pre-Java 7 was mostly predictable. Java 7 (and some
previous versions), does not guaranteee the order of execution,
which can change from run to run, so a deterministic sort was introduced.
As a rule, test method execution should be independent of
one another. However, there may be a number of dependent
tests either through error or by design. This class
allows the user to specify the order of execution of test methods.

There are four possibilities:

MethodSorters.DEFAULT: the default value, deterministic, but not predictable
MethodSorters.JVM: the order in which the tests are returned by the JVM, i.e. there is no sorting done
MethodSorters.NAME_ASC: sorted in order of method name, ascending
MethodSorters.NAME_DESC: sorter in order of method name, descending
  • Loading branch information...
commit c610a49727858597d9a10289bed0a0174053a69f 1 parent db9a456
@matthewfarwell matthewfarwell authored
View
47 src/main/java/org/junit/SortMethodsWith.java
@@ -0,0 +1,47 @@
+package org.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.runners.MethodSorters;
+
+/**
+ * SortMethodsWith allows the user to choose the order of execution of the methods within a test class.
+ * <br/>
+ * <br/>
+ * The default order of execution of JUnit tests within a class is deterministic but not predictable.
+ * Before 4.11, the behaviour was to run the test methods in byte code order, which pre-Java 7 was mostly predictable.
+ * Java 7 (and some previous versions), does not guaranteee the order of execution, which can change from run to run,
+ * so a deterministic sort was introduced.
+ * <br/>
+ * As a rule, test method execution should be independent of one another. However, there may be a number of dependent tests
+ * either through error or by design. This class allows the user to specify the order of execution of test methods.
+ * <br/>
+ * There are four possibilities:
+ * <ul>
+ * <li>MethodSorters.DEFAULT: the default value, deterministic, but not predictable</li>
+ * <li>MethodSorters.JVM: the order in which the tests are returned by the JVM, i.e. there is no sorting done</li>
+ * <li>MethodSorters.NAME_ASC: sorted in order of method name, ascending</li>
+ * <li>MethodSorters.NAME_DESC: sorter in order of method name, descending</li>
+ * </ul>
+ *
+ * Here is an example:
+ *
+ * <pre>
+ * &#064;SortMethodsWith(MethodSorters.NAME_ASC)
+ * public class MyTest {
+ * }
+ * </pre>
+ *
+ * @see org.junit.runners.MethodSorters
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface SortMethodsWith {
+ /**
+ * Optionally specify <code>sorter</code> to have the methods executed in a particular order
+ */
+ MethodSorters value() default MethodSorters.DEFAULT;
+}
View
59 src/main/java/org/junit/internal/MethodSorter.java
@@ -4,32 +4,73 @@
import java.util.Arrays;
import java.util.Comparator;
+import org.junit.SortMethodsWith;
+
public class MethodSorter {
+ /**
+ * DEFAULT sort order
+ */
+ public static Comparator<Method> DEFAULT = new Comparator<Method>() {
+ public int compare(Method m1, Method m2) {
+ int i1 = m1.getName().hashCode();
+ int i2 = m2.getName().hashCode();
+ return i1 != i2 ? i1 - i2 : m1.toString().compareTo(m2.toString());
+ }
+ };
+
+ /**
+ * Method name ascending sort order
+ */
+ public static Comparator<Method> NAME_ASC = new Comparator<Method>() {
+ public int compare(Method m1, Method m2) {
+ return MethodSorter.compare(m1.getName(), m2.getName());
+ }
+ };
+ private static int compare(String s1, String s2) {
+ return s1.compareTo(s2);
+ }
+
/**
- * Gets declared methods of a class in a predictable order.
+ * Method name descending sort order
+ */
+ public static Comparator<Method> NAME_DESC = new Comparator<Method>() {
+ public int compare(Method m1, Method m2) {
+ return MethodSorter.compare(m1.getName(), m2.getName()) * -1;
+ }
+ };
+
+ /**
+ * Gets declared methods of a class in a predictable order, unless @SortMethodsWith(MethodSorters.JVM) is specified.
+ *
* Using the JVM order is unwise since the Java platform does not
* specify any particular order, and in fact JDK 7 returns a more or less
* random order; well-written test code would not assume any order, but some
* does, and a predictable failure is better than a random failure on
- * certain platforms. Uses an unspecified but deterministic order.
+ * certain platforms. By default, uses an unspecified but deterministic order.
* @param clazz a class
* @return same as {@link Class#getDeclaredMethods} but sorted
* @see <a href="http://bugs.sun.com/view_bug.do?bug_id=7023180">JDK
* (non-)bug #7023180</a>
*/
public static Method[] getDeclaredMethods(Class<?> clazz) {
+ Comparator<Method> comparator = getSorter(clazz.getAnnotation(SortMethodsWith.class));
+
Method[] methods = clazz.getDeclaredMethods();
- Arrays.sort(methods, new Comparator<Method>() {
- @Override public int compare(Method m1, Method m2) {
- int i1 = m1.getName().hashCode();
- int i2 = m2.getName().hashCode();
- return i1 != i2 ? i1 - i2 : m1.toString().compareTo(m2.toString());
- }
- });
+ if (comparator != null) {
+ Arrays.sort(methods, comparator);
+ }
+
return methods;
}
private MethodSorter() {}
+ private static Comparator<Method> getSorter(SortMethodsWith sortMethodsWith) {
+ if (sortMethodsWith == null) {
+ return DEFAULT;
+ }
+
+ return sortMethodsWith.value().getComparator();
+ }
}
View
30 src/main/java/org/junit/runners/MethodSorters.java
@@ -0,0 +1,30 @@
+package org.junit.runners;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+import org.junit.internal.MethodSorter;
+
+/**
+ * Sort the methods into a specified execution order
+ */
+public enum MethodSorters {
+ /** Name ascending */
+ NAME_ASC(MethodSorter.NAME_ASC),
+ /** Name descending */
+ NAME_DESC(MethodSorter.NAME_DESC),
+ /** default JVM, (no sort) */
+ JVM(null),
+ /** Default, deterministic but not predictable */
+ DEFAULT(MethodSorter.DEFAULT);
+
+ private final Comparator<Method> fComparator;
+
+ private MethodSorters(Comparator<Method> comparator) {
+ this.fComparator= comparator;
+ }
+
+ public Comparator<Method> getComparator() {
+ return fComparator;
+ }
+}
View
83 src/test/java/org/junit/internal/MethodSorterTest.java
@@ -1,22 +1,59 @@
package org.junit.internal;
+import java.lang.reflect.Method;
import java.util.Arrays;
+
+import org.junit.SortMethodsWith;
import org.junit.Test;
+import org.junit.runners.MethodSorters;
import static org.junit.Assert.*;
public class MethodSorterTest {
+ private static class Dummy {
+ Object alpha(int i, double d, Thread t) {return null;}
+ void beta(int[][] x) {}
+ int gamma() {return 0;}
+ void gamma(boolean b) {}
+ void delta() {}
+ void epsilon() {}
+ }
+ private static class Super {
+ void testOne() {}
+ }
+ private static class Sub extends Super {
+ void testTwo() {}
+ }
+
+ private String toString(Class<?> clazz, Method[] methods) {
+ return Arrays.toString(methods).replace(clazz.getName() + '.', "");
+ }
+
+ private String declaredMethods(Class<?> clazz) {
+ return toString(clazz, MethodSorter.getDeclaredMethods(clazz));
+ }
- @Test public void getDeclaredMethods() throws Exception {
+ @Test public void getMethodsNullSorter() throws Exception {
assertEquals("[void epsilon(), void beta(int[][]), java.lang.Object alpha(int,double,java.lang.Thread), void delta(), int gamma(), void gamma(boolean)]", declaredMethods(Dummy.class));
assertEquals("[void testOne()]", declaredMethods(Super.class));
assertEquals("[void testTwo()]", declaredMethods(Sub.class));
}
-
- private static String declaredMethods(Class<?> c) {
- return Arrays.toString(MethodSorter.getDeclaredMethods(c)).replace(c.getName() + '.', "");
+
+ @SortMethodsWith(MethodSorters.DEFAULT)
+ private static class DummySortWithDefault {
+ Object alpha(int i, double d, Thread t) {return null;}
+ void beta(int[][] x) {}
+ int gamma() {return 0;}
+ void gamma(boolean b) {}
+ void delta() {}
+ void epsilon() {}
}
- private static class Dummy {
+ @Test public void testDefaultSorter() {
+ assertEquals("[void epsilon(), void beta(int[][]), java.lang.Object alpha(int,double,java.lang.Thread), void delta(), int gamma(), void gamma(boolean)]", declaredMethods(DummySortWithDefault.class));
+ }
+
+ @SortMethodsWith(MethodSorters.JVM)
+ private static class DummySortJvm {
Object alpha(int i, double d, Thread t) {return null;}
void beta(int[][] x) {}
int gamma() {return 0;}
@@ -24,11 +61,39 @@ void gamma(boolean b) {}
void delta() {}
void epsilon() {}
}
- private static class Super {
- void testOne() {}
+
+ @Test public void testSortWithJvm() {
+ Class<?> clazz = DummySortJvm.class;
+ String actual = toString(clazz, clazz.getDeclaredMethods());
+
+ assertEquals(actual, declaredMethods(clazz));
+ }
+
+ @SortMethodsWith(MethodSorters.NAME_ASC)
+ private static class DummySortWithNameAsc {
+ Object alpha(int i, double d, Thread t) {return null;}
+ void beta(int[][] x) {}
+ int gamma() {return 0;}
+ void gamma(boolean b) {}
+ void delta() {}
+ void epsilon() {}
}
- private static class Sub extends Super {
- void testTwo() {}
+
+ @Test public void testNameAsc() {
+ assertEquals("[java.lang.Object alpha(int,double,java.lang.Thread), void beta(int[][]), void delta(), void epsilon(), int gamma(), void gamma(boolean)]", declaredMethods(DummySortWithNameAsc.class));
+ }
+
+ @SortMethodsWith(MethodSorters.NAME_DESC)
+ private static class DummySortWithNameDesc {
+ Object alpha(int i, double d, Thread t) {return null;}
+ void beta(int[][] x) {}
+ int gamma() {return 0;}
+ void gamma(boolean b) {}
+ void delta() {}
+ void epsilon() {}
}
+ @Test public void testNameDesc() {
+ assertEquals("[int gamma(), void gamma(boolean), void epsilon(), void delta(), void beta(int[][]), java.lang.Object alpha(int,double,java.lang.Thread)]", declaredMethods(DummySortWithNameDesc.class));
+ }
}
Please sign in to comment.
Something went wrong with that request. Please try again.