JUnit, параметризованные тесты и Spring IoC


Чтобы не подымать руками контекст Spring-а в каждом модульном тесте на JUnit, был придуман SpringJUnit4ClassRunner. Но его использование делает невозможным применение еще одного очень плезного запускальщика тестов: Parameterized. И тут возникает проблема, как усидеть на двух стульях сразу: писать параметризованные тесты и пользоваться Spring.

JUnit,

Положим, у нас есть интерфейс, для которого есть масса разных реализаций. И нам бы хотелось написать тест, проверяющий поведение всех реализаций (убедиться, что все реализации ведут себя одинаково).
            @RunWith(Parameterized.class)
public class ExampleTest {

    @Parameters
    public static Collection getParameters() {
        return parameters;
    }
    
    @Test
    public void test() {
        /* Выполняем проверку поведения */
        asserNotNull(implementation.doSomething());
    }

    public ExampleTest(MyInterface implementation) {
        this.implementation = implementation;
    }
    
    private MyInterface implementation;
    
    private static Collection parameters = Arrays.asList(new Object[] {
            { new MyImplementation1() }, { new MyImplementation2() } });
}
При этом, хотелось бы вынести инстанцирование в конфигурационный файл spring-а.

Что ты делаешь?!

Прежде всего, инициализацию класса с тестами придется взять на себя. И первый порыв поиметь счастье, засунув код инициализации в @BeforeClass public static void setUpClass() заканчивается неудачей.
            @RunWith(Parameterized.class)
public class ExampleTest {

    @BeforeClass
    public static void setUpClass() {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    "classpath:spring-config.xml");
            implementations = (List<String>) context.getBean("implementations");        
    }    

    @Parameters
    public static Collection getParameters() {
        return implementations;
    }
}
         
@Parameters public static Collection getParameters() вызывается раньше.

АХАХАХА!

К тому же, getParameters() должен возвращать коллекцию массивов.
            @RunWith(Parameterized.class)
public class ExampleTest {

    @Parameters
    public static Collection getParameters() {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                    "classpath:spring-config.xml");
        implementations = (List<MyInterface>) context.getBean("arguments");
        return Arrays.asList(new Object[] {implementations.toArray()});
    }
    
    ...
}
Но, очевидно, этот код не корректен в силу того, что мы хотим, чтобы каждый элемент implementations был единственным параметром теста, но по правилам JUnit, параметр теста должен быть элементом массива Object, который, в свою очередь, является элементом коллекции. В приведенном же примере получается так, что каждый элемент implementations является отдельным параметром одного теста. Т.е. на каждый элемент из implementations требуется аргумент в конструкторе.

ПРЕКРАТИ.

            @RunWith(Parameterized.class)
public class ExampleTest {

    @Parameters
    public static Collection getParameters() {
        if (implementations == null) {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    "classpath:spring-config.xml");
            implementations = (List<MyInterface>) context.getBean("implementations");
            parameters = implementations.toArray();
            for (int i = 0; i < parameters.length; i++) {
                parameters[i] = new Object[] { parameters[i] };
            }
        }
        return Arrays.asList(parameters);
    }
    
    @Test
    public void test() {
        /* Выполняем проверку поведения */
        asserNotNull(implementation.doSomething());
    }

    public ExampleTest(MyInterface implementation) {
        this.implementation = implementation;
    }
    
    private MyInterface implementation;
    
    private static Collection<MyInterface> implementations;
    
    private static Object[] parameters;
}
          
Вот в таком виде цель будет достигнута, но чистотой и ясностью такой код уже, увы, не блещет.

Комментариев нет: