質問

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

}
役に立ちましたか?

解決 2

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

他のヒント

for interface

public abstract class AbstractSortTests {
    String[] a ;    

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

   protected abstract Sorter getSorter();

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

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

public class SelectionSortTests extends AbstractSortTests {

    protected Sorter getSorter(){
        return SelectionSort.getInstance();
    }

}

public class QuickSortTests extends AbstractSortTests {

    protected Sorter getSorter(){
        return QuickSort.getInstance();
    }

}

using reflection it is a bit messier but still do-able. I have not tested this code so might have a couple of bugs, but have used this method in the past. Using interfaces would be the preferred method in 99% of cases.

public abstract class AbstractSortTests {
    String[] a ;    

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

   protected abstract Sorter getSorter();

    @Test
    public void arraySortedSingleElement() throws Exception{
        a = new String[]{"A"};
        Assert.assertTrue(executeMethod(getSorterClass(), "isSorted", a);
    }

    @Test
    public void arraySortedDistinctElements() throws Exception{
        a = new String[]{"A","B","C","D"};
        Assert.assertTrue(executeMethod(getSorterClass(), "isSorted", a);
    }

    private void executeMethod(Class<?> sortClass, String methodName, String[] values) throws Exception{
        return sortClass.getDeclaredMethod(methodName, new Class[]{String[].class}).invoke(null, new Object[]{values});
    }
...
}

public class SelectionSortTests extends AbstractSortTests {

    protected Class<?> getSorterClass(){
        return SelectionSort.class;
    }

}

why not something like this?

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

I don't think you have more than 10 different sortings to test. so it should be good.

otherway, you can declare all Sorting classes in Array and load using Class properties.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top