Организация юнит-тестов в Java/ Junit для тестирования классов с общим API

Я реализую некоторые элементарные алгоритмы сортировки (для целей обучения) и хочу написать для них юнит-тесты. Все программы сортировки имеют следующие общие API

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

Итак, я написал следующие тесты для тестирования метода isSorted() в 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));
    }
...
}

Теперь я чувствую, что если бы мне пришлось писать тесты, скажем, InsertionSort,ShellSort и т. Д., Они бы выглядели одинаково. Изменится только имя тестируемого класса.

Итак, как мне организовать тесты? Является ли набор ответом или я могу лучше использовать рефлексию - может быть, написать программу-драйвер, в которую я могу добавить список имен классов для тестирования, и драйвер вызывает запускает общие модульные тесты, передавая ему имя класса.,

Я понимаю, что это обычная ситуация. Хотелось бы узнать, как это можно сделать без слюны или виолончели

ОБНОВЛЕНИЕ: спасибо @BevinQ и @Matthew Farwell, я попытался решить эту проблему с помощью параметризованных модульных тестов. Использовал отражение для вызова статического метода. Кажется, что работает:), хотя я думаю, что он все еще может быть реорганизован, чтобы избежать дублирования кода

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

}

3 ответа

Решение

Как говорит @BevynQ, вы значительно упростите себе жизнь, если сделаете ваши методы нестатичными и реализуете интерфейс (называемый Sorter ниже). Вы можете легко использовать Parameterized. Это очень быстрый пример того, как его использовать (непроверенный, не скомпилированный)

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

Для интерфейса

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

}

используя отражение, это немного более грязно, но все же выполнимо. Я не тестировал этот код, поэтому может иметь несколько ошибок, но использовал этот метод в прошлом. Использование интерфейсов будет предпочтительным методом в 99% случаев.

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

}

Почему не как то так?

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

Я не думаю, что у вас есть более 10 различных сортировок для тестирования. так должно быть хорошо.

в противном случае вы можете объявить все классы сортировки в массиве и загрузить их, используя свойства класса.

Другие вопросы по тегам