Общая статическая инициализация в JUnit Suite с использованием PowerMock

Я работаю с большим набором тестов для старой кодовой базы Java. Короче говоря, он использует DBUnit для загрузки статических наборов данных только для чтения с локального жесткого диска. В настоящее время это делается на уровне каждого теста, а это означает, что запуск пакета занимает смехотворно много времени.

Я пытаюсь сделать общий статический класс общим на уровне набора. (У нас также не было определенного набора тестов — я сделал его с помощью ClasspathSuite)

Еще одна проблема заключается в том, что все тесты используют @RunWith(PowerMockRunner.class) - поэтому иногда возникают проблемы с путями к классам, которые искажают то, что, как я думаю, обычно решает проблемы.

Вот простой случай того, что не работает.


Тестируемый Java-код

Статическая зависимость в кодовой базе

package com.somecorp.proj;
public class SomeDependency {
    public static String getStaticString() {
        // some resource intensive process we don't want running in unit tests
        return "real value";
    }
}

Тестируемый класс 1

package com.somecorp.proj;

public class UnderTest {

    public String getIt() {
        return "Here is the value: " + SomeDependency.getStaticString();
    }
}

Тестируемый класс 2

package com.somecorp.proj;

public class AlsoUnderTest {
    public String getTheThing() {
        return "some other value using it: " + SomeDependency.getStaticString();
    }
}

JUnit-код

Код с методом инициализации, который я хочу запускать только ОДИН РАЗ в начале запуска пакета

package com.somecorp.proj.testClasses;

public class StaticTestClassRequiringInitialization {

    private static String testString;

    public static void init() {
        // Some expensive stuff
        System.out.println("EXPENSIVE INITIALIZATION");
        testString = "a test string";
    }

    public static String getTestString() {
        return testString;
    }
}

Тест 1

package com.somecorp.proj;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization;

@RunWith(PowerMockRunner.class)
@PrepareForTest(SomeDependency.class)
public class TestUnderTest {

    @Before
    public void setUp() {
        PowerMockito.mockStatic(SomeDependency.class);
        PowerMockito.when(SomeDependency.getStaticString()).
            thenReturn(StaticTestClassRequiringInitialization.getTestString());
    }

    @Test
    public void testGetIt() {
        UnderTest ut = new UnderTest();
        assertEquals(
            "Here is the value: a test string",
            ut.getIt()
        );
    }
}

Тест 2

package com.somecorp.proj;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization;

@RunWith(PowerMockRunner.class)
@PrepareForTest(SomeDependency.class)
public class TestAlsoUnderTest {

    @Before
    public void setUp() {
        PowerMockito.mockStatic(SomeDependency.class);
        PowerMockito.when(SomeDependency.getStaticString()).
            thenReturn(StaticTestClassRequiringInitialization.getTestString());
    }

    @Test
    public void testGetTheThing() {
        AlsoUnderTest ut = new AlsoUnderTest();
        assertEquals(
            "some other value using it: a test string",
            ut.getTheThing()
        );
    }
}

Тестовый набор

package com.somecorp.proj;

import static org.junit.extensions.cpsuite.SuiteType.RUN_WITH_CLASSES;
import static org.junit.extensions.cpsuite.SuiteType.TEST_CLASSES;

import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.BeforeSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters;
import org.junit.extensions.cpsuite.ClasspathSuite.SuiteTypes;
import org.junit.runner.RunWith;

import com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization;

@RunWith(ClasspathSuite.class)
@SuiteTypes({RUN_WITH_CLASSES, TEST_CLASSES})
@ClassnameFilters({".*Test.*"})
public class ProjectJUnitSuite {

    @BeforeSuite
    public static void setUpBeforeSuite() {
        StaticTestClassRequiringInitialization.init();
    }

}

Подробности JAR

  • powermock-mockito-1.4.12-full.jar
  • мокито-все-1.9.0.jar
  • cpsuite-1.2.6.jar
  • javassist-3.16.1-GA.jar
  • Использование Junit 4.8.1

И трассировка сбоя теста (в частности, не ошибка - сбой) (для одного теста... второй практически идентичен):

org.junit.ComparisonFailure: expected:<...her value using it: [a test string]> but was:<...her value using it: [null]>
    at org.junit.Assert.assertEquals(Assert.java:123)
    at org.junit.Assert.assertEquals(Assert.java:145)
    at com.somecorp.proj.TestAlsoUnderTest.testGetTheThing(TestAlsoUnderTest.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    at java.lang.reflect.Method.invoke(Method.java:611)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:112)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:73)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:42)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:24)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.junit.extensions.cpsuite.ClasspathSuite.run(ClasspathSuite.java:196)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Как я могу заставить этот общий статический инициализатор запускаться один раз для каждого пакета и на него можно ссылаться во всех моих модульных тестах с поддержкой Powermock?


person Kevin Rooney    schedule 22.11.2013    source источник
comment
Можете ли вы поделиться исходным кодом, чтобы мы могли поиграть с ним?   -  person Haim Raman    schedule 25.11.2013


Ответы (1)


Я получил инициализацию только один раз, используя аннотацию @PowerMockIgnore для всех тестовых классов, ссылающихся на StaticTestClassRequiringInitialization.

В этом случае, в частности, добавление приведенной ниже аннотации к обоим тестовым классам JUnit поможет.

@PowerMockIgnore("com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization")

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

  • Слишком широко. Классы, которые нужно было имитировать, больше не загружались загрузчиком классов powermock, потому что я передал весь пакет в PowerMockIgnore.
  • Недостаточно широкий. Присутствовало несколько более сложных зависимостей, чем в приведенном выше примере, требующих, чтобы я игнорировал некоторые другие классы. Результатом были некоторые странные ошибки несоответствия загрузки классов.

Это не будет работать во всех случаях (в частности, это не будет работать, если StaticTestClassRequiringInitialization использует классы, которые также имитируются в тесте), но в этом случае это работает.

Я также провел некоторое исследование PowerMockAgent, чтобы избежать PowerMockRunner, и поэтому многие всех связанных проблем с загрузкой классов, но тестируемый рабочий код * требовал подавления статических инициализаторов, поэтому это не было стартовым.

* Извиняюсь за то, что не поделился полным исходным кодом, таковы опасности задавать вопросы по большим проприетарным кодовым базам.

person Kevin Rooney    schedule 17.12.2013