JUnit5: тестируйте несколько классов без повторения кода

Я создал собственную реализацию стека на Java, которая выглядит примерно так:

Есть интерфейс «Стек», который предоставляет основные функции (поп, толчок, просмотр и т. Д.). И затем у меня есть 2 конкретных класса, один с помощью массивов, а другой со связанным списком (как в данном случае не важно).

Теперь мой вопрос: я хочу проверить это с помощью JUnit5, и поскольку вы не можете создать экземпляр интерфейса, я должен тестировать каждую функцию один раз для класса с массивами и один раз для класса со связанным списком, поэтому код излишне длинный . Есть ли способ протестировать все функции интерфейса или чего-то подобного? Потому что, если бы теперь была добавлена ​​третья реализация, мне пришлось бы все это переписать заново.

Я уже пробовал «ParameterizedTests», но не добился никакого прогресса.

Буду рада помощи!


person Joey    schedule 24.11.2017    source источник
comment
@ParameterizedTest должен быть подходящим вариантом - какая у вас проблема когда попробуешь?   -  person Alexandre Dupriez    schedule 24.11.2017
comment
Теперь я знаю, как правильно им пользоваться. Я просто не нашел хорошего руководства по JUnit5.   -  person Joey    schedule 24.11.2017
comment
Может быть, опубликовать минимальный пример кода, чтобы мы могли проверить, в чем проблема?   -  person Alexandre Dupriez    schedule 24.11.2017
comment
Я думаю, было бы достаточно, если бы вы могли просто дать мне немного синтаксиса кода, который показывает, как я могу использовать два класса с @ParameterizedTest.   -  person Joey    schedule 24.11.2017


Ответы (3)


Я не знаю, с какой проблемой с @ParameterizedTest вы столкнулись, но, как вы просили, это очень общий тестовый пример, который может быть полезен для вашего теста:

import static org.junit.jupiter.api.Assertions.assertEquals;  
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
...

public static Stream<Arguments> provideStacks() {
  return Stream.of(
      Arguments.of(new ArrayStack()),
      Arguments.of(new LinkedStack())
  );
}

@ParameterizedTest
@MethodSource("provideStacks")
public void test(Stack stack) {
  stack.push(1);
  assertEquals(1, stack.pop());
}

public interface Stack {
  void push(int i);
  int pop();
}

public static final class ArrayStack implements Stack {
  @Override
  public void push(int i) {
  }

  @Override
  public int pop() {
    return 1;
  }
}

public static final class LinkedStack implements Stack {
  @Override
  public void push(int i) {
  }

  @Override
  public int pop() {
    return 1;
  }
}
person Alexandre Dupriez    schedule 24.11.2017
comment
Большое спасибо, но при запуске кода возникает ошибка: java.lang.AbstractMethodError - person Joey; 24.11.2017
comment
Эээ ... У вас есть полная трассировка стека, пожалуйста? Кроме того, как запустить тест? Я только что запустил его в Eclipse, и все прошло хорошо. - person Alexandre Dupriez; 24.11.2017
comment
Я обновил свой пост, добавил ошибки и запустил его в IntelliJ. - person Joey; 24.11.2017
comment
@Joey: Я вижу, у вас может быть ошибка с IntelliJ - проверьте свою версию JUnit и IntelliJ, это обновление для поддержки JUnit 5 M4 в IntelliJ. - person Alexandre Dupriez; 24.11.2017
comment
Я обновил JUnit и IntelliJ, теперь другая ошибка. Я обновил свой пост и добавил новую ошибку. - person Joey; 24.11.2017
comment
@Joey: хорошо - это потому, что он работает с JUnit 4. Убедитесь, что IntelliJ использует JUnit 5, и вы используете тот же импорт, что и те, которые я добавил выше. - person Alexandre Dupriez; 24.11.2017

Тестовые интерфейсы Была бы другая возможность. Вы должны определить свои тесты как методы интерфейса по умолчанию и реализовать интерфейс один раз для каждой реализации Stack. Каждая реализация может добавлять дополнительные тесты и т. Д.

interface StackContractTests {

    Stack newEmptyStack();

    @Test
    default void popsWhatWasLastPushed() {
        Stack stack = newEmptyStack();
        stack.push("foo");
        assertEquals("foo", stack.pop());
    }

    @Test
    default void cannotPopFromEmptyStack() {
        Stack stack = newEmptyStack();
        assertThrows(EmptyStackException.class, stack::pop);
    }
}

public class ArrayListBasedStackTests implements StackContractTests {
    @Override
    public Stack newEmptyStack() {
        return new ArrayListBasedStack();
    }
}

public class LinkedListBasedStackTests implements StackContractTests {
    @Override
    public Stack newEmptyStack() {
        return new LinkedListBasedStack();
    }
}
person Marc Philipp    schedule 25.11.2017

Сделайте частный метод, который выполняет тест с учетом типа интерфейса в качестве параметра

private void testStack(Stack stack) {...}

Затем вызовите его в модульном тесте:

@Test
public void testImplementations() {
     testStack(new ListStack());
     testStack(new LinkedListStack());
}
person DeiAndrei    schedule 24.11.2017