Тестирование несколько реализаций интерфейса с тесты 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 {}