I am writing template method pattern for Sorting (SelectionSort
, InsertionSort
and QuickSort
). I wrote SelectionSort
interface, following by its implementation (SelectionSortImpl
) and then its unit tests with JUnit (SelectionSortImplTest
).
As a next step, I refactored to extract template method. I put the only sort()
method from SelectionSort
interface, put it in a new interface AbstractSort
, implemented its abstract class namely AbstractSortImpl
.
Now, I realize that the SelectionSortImplTest
contains unit tests which would be common to all implementations (SelectionSort
, InsertionSort
, and QuickSort
). I want to refactor SelectionSortImplTest
, such that it becomes AbstractSortImpl
. The idea is to change only one line of code in my @BeforeEach
setup from the type of sorting technique (such as from SelectionSortImpl
to InsertionSortImpl
).
How can I do this?
Following my code:
package algorithms.sorting;
public interface AbstractSort {
void sort(int[] arr);
}
package algorithms.sorting;
import java.util.Arrays;
public abstract class AbstractSortImpl implements AbstractSort {
protected int[] inputArr;
@Override
public final void sort(int[] arr) {
this.inputArr = arr;
sort();
}
protected abstract void sort();
protected final void swap(int i, int j) {
int temp = inputArr[i];
inputArr[i] = inputArr[j];
inputArr[j] = temp;
}
public static void printArray(int[] arrToPrint) {
System.out.println(Arrays.toString(arrToPrint));
}
}
package algorithms.sorting;
public class SelectionSortImpl extends AbstractSortImpl {
@Override
protected void sort() {
int minValueIndex;
for(int candidateIndex = 0; candidateIndex < inputArr.length - 1; candidateIndex++) {
minValueIndex = findMinValueIndex(candidateIndex);
swap(candidateIndex, minValueIndex);
}
}
private int findMinValueIndex(int index) {
int minValueIndex = index;
for (int indexer = index; indexer < inputArr.length; indexer++) {
if (inputArr[indexer] < inputArr[minValueIndex]) {
minValueIndex = indexer;
}
}
return minValueIndex;
}
}
package algorithms.sorting;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
class SelectionSortImplTest {
public static final int NO_OF_ENTRIES = 10;
private int[] uut;
private SelectionSortImpl selectionSort;
@BeforeEach
public void setup() {
selectionSort = new SelectionSortImpl();
}
@Test
public void sort_EMPTY() {
uut = generateRandomArray(0);
runTest();
}
@Test
public void sort_NON_EMPTY_UNSORTED() {
uut = generateRandomArray(NO_OF_ENTRIES);
runTest();
}
private void runTest() {
int expected, actual;
// Test preparation
expected = 0;
int[] copiedUut = new int[uut.length];
System.arraycopy(uut, 0, copiedUut, 0, uut.length);
Arrays.sort(copiedUut);
// Actual method execution
selectionSort.sort(uut);
actual = Arrays.compare(copiedUut, uut);
// Verification
SelectionSortImpl.printArray(uut);
SelectionSortImpl.printArray(copiedUut);
Assertions.assertEquals(expected, actual);
}
private int[] generateRandomArray(int noOfEntries) {
return new Random().ints(0,350).limit(noOfEntries).toArray();
}
@Test
public void sort_NON_EMPTY_SORTED() {
uut = generateRandomArray(NO_OF_ENTRIES);
Arrays.sort(uut);
runTest();
}
@Test
public void sort_NON_EMPTY_UNSORTED_DESCENDING() {
uut = generateRandomArray(NO_OF_ENTRIES);
Arrays.sort(new int[][]{uut}, Collections.reverseOrder());
runTest();
}
@Test
public void sort_ONE_ELEMENT_ONLY() {
uut = generateRandomArray(1);
runTest();
}
}
PS: It is clear to me that I could manually go on the change lines private SelectionSortImpl selectionSort;
and selectionSort = new SelectionSortImpl();
to AbstractSortImpl sorter;
and sorter = new InsertionSortImpl();
respectively next time I have an implementations for InsertionSort
. But this would be contrary to the idea of automated testing.