Тестирование несколько реализаций интерфейса с тесты JUnit4

Я хочу запустить те же тесты JUnit для разных реализаций интерфейса. Я нашел хорошее решение с параметр@:

public class InterfaceTest{

  MyInterface interface;

  public InterfaceTest(MyInterface interface) {
    this.interface = interface;
  }

  @Parameters
  public static Collection<Object[]> getParameters()
  {
    return Arrays.asList(new Object[][] {
      { new GoodInterfaceImpl() },
      { new AnotherInterfaceImpl() }
    });
  }
}

этот тест будет выполняться дважды, сначала с GoodInterfaceImpl затем с AnotherInterfaceImpl класса. Но проблема в том, что для большинства тестов мне нужен новый объект. Упрощенный пример:

@Test
public void isEmptyTest(){
   assertTrue(interface.isEmpty());
}

@Test
public void insertTest(){
   interface.insert(new Object());
   assertFalse(interface.isEmpty());
}

Если isEmptyTest запускается после insertTest это не удается.

есть ли возможность автоматически запускать каждый testcase с новым экземпляром реализации?

BTW: реализация очистить() или сброс()-метод для интерфейса на самом деле не является параметрами, так как он не нужен в производительном коде.

4 ответов


создайте заводской интерфейс и реализации, возможно, только в вашей тестовой иерархии, если вам не нужна такая вещь в производстве, и сделайте getParameters() верните список заводов.

затем вы можете вызвать фабрику в @Before аннотированный метод, чтобы получить новый экземпляр вашего фактического класса под тест для каждого метода испытания.


вот еще один подход с шаблоном метода шаблона:

интерфейсно-ориентированные тесты переходят в базовый класс:

public abstract class MyInterfaceTest {

    private MyInterface myInterface;

    protected abstract MyInterface makeContractSubject();

    @Before
    public void setUp() {
        myInterface = makeContractSubject();
    }

    @Test
    public void isEmptyTest(){
        assertTrue(myInterface.isEmpty());
    }

    @Test
    public void insertTest(){
        myInterface.insert(new Object());
        assertFalse(myInterface.isEmpty());
    }
}

для каждого конкретного класса определите конкретный тестовый класс:

public class GoodInterfaceImplTest extends MyInterfaceTest {

    @Override
    protected MyInterface makeContractSubject() {
        // initialize new GoodInterfaceImpl
        // insert proper stubs
        return ...;
    }

    @Test
    public void additionalImplementationSpecificStuff() {
        ...
    }
}

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

кстати, для того чтобы этот подход работал, интерфейс конструируйте в путе который позволяет испытать методами интерфейса только. Это подразумевает тестирование на основе состояния - вы не можете проверить mocks в базовом тестовом классе. Если вам нужно проверить mocks в тестах, специфичных для реализации, эти тесты должны перейти в конкретные классы тестов.


на всякий случай, если кто-то доберется сюда(как и я), ища тестирование нескольких реализаций одного и того же интерфейса в .net, вы можете увидеть один из подходов, которые я использовал в одном из проектов здесь

ниже то, что мы следуем вкратце

одна и та же dll тестового проекта запускается дважды с помощью vstest.консоль, установив переменную среды. Внутри теста (либо в инициализации сборки, либо в инициализации теста) зарегистрируйте соответствующие реализации в контейнер IoC на основе значения переменной среды.


в Junit 5 Вы можете сделать:

@ParameterizedTest
@MethodSource("myInterfaceProvider")
void test(MyInterface myInterface) {}

static Stream<MyInterface> myInterfaceProvider() {
   return Stream.of(new ImplA(), new ImplB());
}

interface MyInterface {}

static class ImplA implements MyInterface {}

static class ImplB implements MyInterface {}