Статьи

Тестирование абстрактных классов и шаблона шаблона

Из википедии «Шаблонный метод определяет программный каркас алгоритма. Один или несколько шагов алгоритма могут быть переопределены подклассами, чтобы обеспечить различное поведение при одновременном соблюдении всеохватывающего алгоритма ».

Обычно этот шаблон состоит из двух или более классов, один из которых является абстрактным классом, предоставляющим шаблонные методы (неабстрактные), которые имеют вызовы абстрактных методов, реализованных одним или несколькими конкретными подклассами.

Часто абстрактный класс шаблона и конкретные реализации находятся в одном и том же проекте, но в зависимости от объема проекта эти конкретные объекты будут реализованы в другом проекте.

В этой статье мы увидим, как тестировать шаблонный шаблонный метод, когда конкретные классы реализованы во внешнем проекте, или более общий, как тестировать абстрактные классы.

Давайте посмотрим на простой пример шаблона метода шаблона. Рассмотрим класс, отвечающий за получение вектора целых чисел, и вычислим евклидову норму. Эти целые числа могут быть получены из нескольких источников и оставлены для каждого проекта, чтобы обеспечить способ их получения.

Шаблонный класс выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public abstract class AbstractCalculator {
 
 public double euclideanNorm() {
 
  int[] vector = this.read();
 
  int total = 0;
 
  for(int element:vector) {
   total+= (element*element); 
  }
 
  return Math.sqrt(total);
 }
 
 public abstract int[] read();
}

Теперь другой проект может расширить предыдущий класс и реализовать реализацию абстрактного калькулятора, предоставив реализацию метода read ().

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class ConsoleCalculator extends AbstractCalculator {
 
 public int[] read() {
 
  int [] data = new int[0];
 
  Scanner scanner = new Scanner(System.in);
 
  //data = read requried data from console
 
  return data;
 
 }
 
}

Разработчик, написавший конкретную реализацию, будет тестировать только метод read (), он может «поверить», что разработчик абстрактного класса протестировал неабстрактные методы.

Но как мы собираемся писать модульные тесты по методу вычисления, если класс является абстрактным и требуется реализация метода read ()?

Первый подход может быть создание поддельной реализации:

01
02
03
04
05
06
07
08
09
10
11
12
13
public class FakeCalculator extends AbstractCalculator {
 
 private int[] data;
 
 public FakeCalculator(int[] data) {
  this.data = data;
 }
 
 public int[] read() {
  return this.data;
 }
 
}

Это не плохой подход, но имеет некоторые недостатки:

  • Тест будет менее читабельным, читатели должны знать о существовании этих поддельных классов и точно знать, что они делают.
  • Как автор тестов вы будете тратить время на реализацию поддельных классов, в данном случае это просто, но ваш проект может иметь более одного абстрактного класса без реализации или даже с несколькими абстрактными методами.
  • Поведение поддельных классов «жестко запрограммировано».

Лучше использовать Mockito для макетирования только абстрактного метода, в то время как вызывается реализация неабстрактных методов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class WhenCalculatingEuclideanNorm {
 
 @Test
 public void should_calculate_correctly() {
 
  AbstractCalculator abstractCalculator = mock(AbstractCalculator.class, Mockito.CALLS_REAL_METHODS);
 
  doReturn(new int[]{2,2}).when(abstractCalculator).read();
  assertThat(abstractCalculator.euclideanNorm(), is(2.8284271247461903));
 
 }
 
 @Test
 public void should_calculate_correctly_with_negative_values() {
 
  AbstractCalculator abstractCalculator = mock(AbstractCalculator.class, Mockito.CALLS_REAL_METHODS);
 
  doReturn(new int[]{-2,-2}).when(abstractCalculator).read();
  assertThat(abstractCalculator.euclideanNorm(), is(2.8284271247461903));
 
 }
 
}

Mockito упрощает тестирование абстрактных классов, вызывая реальные методы и только заглушая абстрактные методы. Обратите внимание, что в этом случае, потому что мы вызываем реальные методы по умолчанию, вместо использования типичной структуры when () then (), должна использоваться схема doReturn.

Конечно, этот подход можно использовать только в том случае, если ваш проект не содержит конкретной реализации алгоритма или ваш проект будет частью сторонней библиотеки другого проекта. В других случаях лучшим способом решения проблемы является тестирование реализованного класса.

Скачать исходный код

Ссылка: тестирование абстрактных классов (и шаблона шаблонного метода в частности) от нашего партнера по JCG Алекса Сото в блоге One Jar To Rule All .