Search code examples
javajunitorganization

organizing unittests in java/junit for testing classes with common api


I am implementing some elementary sorting algorithms (for the purpose of learning) ,and want to write unittests for them .All the sorting programs have the following common api

...
public static void sort(Comparable[] a);
...
public static boolean isSorted(Comparable[] a);
...
public static boolean isSorted(Comparable[] a),int from ,int to;
...

So,I wrote the following tests for testing the isSorted() method in SelectionSort

public class SelectionSortTests {
        String[] a ;    

    @After
    public void tearDown() throws Exception {
            a = null;
    }

    @Test
    public void arraySortedSingleElement(){
        a = new String[]{"A"};
        Assert.assertTrue(SelectionSort.isSorted(a));
    }

    @Test
    public void arraySortedDistinctElements(){
        a = new String[]{"A","B","C","D"};
        Assert.assertTrue(SelectionSort.isSorted(a));
    }
    @Test
    public void arrayNotSorted(){
        a = new String[]{"A","B","C","B"};
        Assert.assertFalse(SelectionSort.isSorted(a));
    }
...
}

Now I feel that if I were to write tests for say InsertionSort,ShellSort etc ,they would look the same..Only the name of the class under test will change..

So,how should I organize the tests? Is a suite the answer or can I do better using reflection - may be write a driver program to which I can add a list of names of classes to be tested, and the driver invokes runs the common unit tests by passing the classname to it..

I realize this is a common situation..would like to know how this can be handled without spittle or cellotape

UPDATE: thanks @BevinQ and @Matthew Farwell ,I tried to solve this using Parameterized unit tests. Used reflection to call the static method .. Seems to work :) though I think it can still be refactored to avoid duplicate code

@RunWith(Parameterized.class)
public class ParameterizedSortTests {
    private Class classToTest;
    private Method methodToTest;

    public ParameterizedSortTests(String packageName,String classToTest) {
        super();
        try {
            this.classToTest = Class.forName(packageName+"."+classToTest);
        } catch (ClassNotFoundException e) {
            System.out.println("failed to get class!!");
            e.printStackTrace();
        }

    }

    //method return collection of class names to be tested
    @Parameterized.Parameters
    public static  List<Object[]> classesToTest(){
        return Arrays.asList(new Object[][]{ 
                {"elemsorts","SelectionSort"} ,
                {"elemsorts","InsertionSort"} 
        });
    }


    public void setMethod(String method,Class...args){
        try {
            this.methodToTest = this.classToTest.getMethod(method, args);
        } catch (SecurityException e) {

            e.printStackTrace();
        } catch (NoSuchMethodException e) {

            e.printStackTrace();
        }
    }

    @Test
    public void arrayIsSorted(){
        setMethod("isSorted",Comparable[].class);
        String[] a = new String[]{"A","B","C","D"};
        Boolean arraySorted = null;
        try {
            arraySorted = (Boolean)this.methodToTest.invoke(null, new Object[]{a});
            System.out.println(this.methodToTest+"returned :"+arraySorted);
        } catch (IllegalArgumentException e) {

            e.printStackTrace();
        } catch (IllegalAccessException e) {

            e.printStackTrace();
        } catch (InvocationTargetException e) {

            e.printStackTrace();
        }

        Assert.assertTrue(arraySorted);
    }

    @Test
    public void arrayIsNotSorted(){
        setMethod("isSorted",Comparable[].class);
        String[] a = new String[]{"A","B","C","B"};
        Boolean arraySorted = null;
        try {
            arraySorted = (Boolean)this.methodToTest.invoke(null, new Object[]{a});
            System.out.println(this.methodToTest+"returned :"+arraySorted);
        } catch (IllegalArgumentException e) {

            e.printStackTrace();
        } catch (IllegalAccessException e) {

            e.printStackTrace();
        } catch (InvocationTargetException e) {

            e.printStackTrace();
        }
        //System.out.println("arraySorted="+arraySorted);
        Assert.assertFalse(arraySorted);
    }   

}

Solution

  • As @BevynQ says, you'll make life a lot easier for yourself if you make your methods non-static, and you implement an interface (called Sorter below). The you can easily use Parameterized. This is a very quick example of how to use it, (untested, uncompiled)

    @RunWith(Parameterized.class)
    public class SorterTest {
      @Parameters
      public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[][] {
          { new SelectionSort() },
          { new BubbleSort() }
        });
      }
    
      private final Sorter sorter
    
      public SorterTest(Sorter sorter) {
        this.sorter = sorter;
      }
    
      @Test
      public void arraySortedSingleElement(){
        String[] a = new String[]{"A"};
        Assert.assertTrue(sorter.isSorted(a));
      }
    
      @Test
      public void arraySortedDistinctElements(){
        String[] a = new String[]{"A","B","C","D"};
        Assert.assertTrue(sorter.isSorted(a));
      }
    
      @Test
      public void arrayNotSorted(){
        String[] a = new String[]{"A","B","C","B"};
        Assert.assertFalse(sorter.isSorted(a));
      }
    }