Организация юнит-тестов в 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 различных сортировок для тестирования. так должно быть хорошо.
в противном случае вы можете объявить все классы сортировки в массиве и загрузить их, используя свойства класса.