Вопрос об архитектуре модульных тестов

Таким образом, я запустил к модульным тестам расположения на следующий бит кода:

public interface MyInterface {
  void MyInterfaceMethod1();
  void MyInterfaceMethod2();
}

public class MyImplementation1 implements MyInterface {
  void MyInterfaceMethod1() {
    // do something
  }

  void MyInterfaceMethod2() {
    // do something else
  }

  void SubRoutineP() {
    // other functionality specific to this implementation
  }
}

public class MyImplementation2 implements MyInterface {
  void MyInterfaceMethod1() {
    // do a 3rd thing
  }

  void MyInterfaceMethod2() {
    // do something completely different
  }

  void SubRoutineQ() {
    // other functionality specific to this implementation
  }
}

с несколькими реализациями и ожиданием больше для прибытия.

Моя начальная буква думала, должен был сохранить меня модульные тесты перезаписи времени с чем-то вроде этого:

public abstract class MyInterfaceTester {
  protected MyInterface m_object;  

  @Setup
  public void setUp() {
    m_object = getTestedImplementation();
  }

  public abstract MyInterface getTestedImplementation();

  @Test
  public void testMyInterfaceMethod1() {
    // use m_object to run tests
  }

  @Test
  public void testMyInterfaceMethod2() {
    // use m_object to run tests
  }
}

который я мог затем разделить на подклассы легко для тестирования реализации определенные дополнительные методы как так:

public class MyImplementation1Tester extends MyInterfaceTester {
  public MyInterface getTestedImplementation() {
    return new MyImplementation1();
  }

  @Test
  public void testSubRoutineP() {
    // use m_object to run tests
  }
}

и аналогично для implmentation 2 вперед.

Таким образом, мой вопрос действительно: разве там какая-либо причина не состоит в том, чтобы сделать этого? JUnit, кажется, нравится он очень хорошо, и это удовлетворяет мои потребности, но я действительно не видел ничего как он ни в одной из книг поблочного тестирования и примеров, которые я читал.

Есть ли некоторая лучшая практика, которую я невольно нарушаю? I собирается для страдания в будущем? Существует ли просто намного лучший путь там, который я не рассмотрел?

Спасибо за любую справку.

15
задан Tom Tresansky 8 June 2010 в 16:54
поделиться

3 ответа

есть ли причина не делать этого?

Нет. Сделай это. Именно по этой причине тесты являются классами для .

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

Продолжайте читать. Во введении это не покрывается.

Есть ли какие-то передовые методы, которые я невольно нарушаю?

Нет.

Готовлю ли я себя к душевной боли в будущем?

Нет.

Некоторые люди нервничают по поводу «хрупких тестов». Здесь вы можете найти несколько вопросов о том, как сделать так, чтобы изменение программного обеспечения не приводило к изменению тестов. В конечном счете, попытки создать «надежные» тесты - это глупо. Вы хотите, чтобы тесты были написаны так, чтобы каждое небольшое изменение на видимом уровне интерфейса программного обеспечения требовало перезаписи теста.

Вам нужны тесты, чтобы невидимые внутренние изменения не требовали перезаписи теста.

Использование классов и подклассов противоречит этим соображениям.

Есть просто лучший выход, который я не рассматривал?

Нет. Объектная ориентация - это точка. Именно по этой причине тесты являются классом.

18
ответ дан 1 December 2019 в 03:04
поделиться

Хотя я поддерживаю SLott на 100%, я бы также рассмотрел параметризованные тесты JUnit вместо иерархии тестовых классов для этого:

@RunWith(Parameterized.class)
public class MyInterfaceTester {
  private MyInterface m_object;  

  public void MyInterfaceTester(MyInterface object) {
    m_object = object;
  }

  @Parameters
  public static Collection<Object[]> data() {
    List<Object[]> list = new ArrayList<Object[]>();

    list.add(new Object[]{new MyImplementation1()});
    list.add(new Object[]{new MyImplementation2()});

    return list;
  }

  @Test
  public void testMyInterfaceMethod1() {
    // use m_object to run tests
  }

  @Test
  public void testMyInterfaceMethod2() {
    // use m_object to run tests
  }
}

Нет необходимости в иерархии тестовых классов: просто добавьте новую реализацию, добавив еще один элемент списка в data метод.

5
ответ дан 1 December 2019 в 03:04
поделиться

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

1
ответ дан 1 December 2019 в 03:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: