当前位置: 首页 > 知识库问答 >
问题:

使用PowerMock在JUnit套件中共享静态初始化

孟建木
2023-03-14

我正在为一个旧的Java代码库使用一个大型测试套件。长话短说,它使用DBUnit从本地硬盘上传静态只读数据集。目前,这是在每个测试级别上完成的,这意味着套件需要相当长的时间才能运行。

我试图创建一个共享的静态类,在套件级别共享。(我们也没有定义合适的测试套件——我用ClasspathSuite做了一个)

另一个问题是所有的测试都使用@RunBy(PowerMockRunner.class)——所以偶尔会有类路径问题搞砸我认为通常可以解决的问题。

这是一个不起作用的简单案例。

代码库中的静态依赖项

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();
    }
}

使用init方法编写的代码,我希望在套件运行开始时只运行一次

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();
    }

}

罐子细节

  • powermock-mockito-1.4.12-full.jar
  • 模拟-all-1.9.0.jar
  • 铜绿-1.2.6.jar
  • 3.16.1-GA.jar
  • 使用君尼特 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的单元测试中引用?

共有1个答案

司徒翼
2023-03-14

我通过在所有引用StaticTestClassRequest iring初始化的测试类上使用@PowerMockIgnore注释使初始化只发生一次。

特别是在这种情况下,将下面的注释添加到两个JUnit测试类就可以了。

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

我以前尝试过这个,但它最初不起作用,因为我最初通过的论点是:

    < li >范围太广——需要模拟的类不再被powermock classloader加载,因为我将整个包传递给了PowerMockIgnore < li >不够广泛——存在一些比上述示例更复杂的依赖关系,需要我忽略一些其他类。结果是一些奇怪的类加载不匹配错误。

这不会在所有情况下都起作用(特别是如果StaticTestClassRequest iring初始化使用在测试中也被模拟的类,它就不起作用),但在这种情况下它确实起作用。

我还对 PowerMockAgent 进行了一些调查,以避免 PowerMockRunner,因此完全避免了许多相关的类加载问题,但是所测试的生产代码*需要抑制静态初始化器,因此它无法启动。

*对于没有共享完整的源代码表示歉意,例如为大型专有代码库提出问题的危险。

 类似资料:
  • Powermock为什么不模拟静态方法调用,而是在然后()语句中调用初始方法? 在这种情况下,我有一系列方法调用: TestClass方法-调用- Class4方法尝试查找上下文中不存在且挂起的对象,因此我尝试使用Powermock模拟公共静态Class3方法。 所有的类和方法都是非最终的。我使用TestNg。我的测试方法有一个@准备测试我尝试了以下方法来模拟方法调用: 或而不是当-然后返回: 或

  • 问题内容: 这是一段Java代码: 它如何编译?初始化后已执行变量“ ture”的声明。据我所知,静态块和字段已经按照它们出现的顺序执行了。 现在,为什么实例块中的值9已被打印3次?顺便说一句,该类的实例已创建了3次。那不是功课,我正在学习Java进行认证。 问题答案: 关于第一个问题,静态块确实按照它们出现的顺序进行处理,但是在处理静态块之前,先处理声明。声明作为类 准备工作 的一部分(JLS§

  • 这是一个与这篇文章相似的问题。我认为最有希望的答案与模板化静态初始化有关。这是该答案中的类: 用法: 这适用于结构或类以及基类型。我想做的是将其与

  • 问题内容: 我想知道为什么默认情况下C,C ++和Java中的确切静态变量初始化为零?为什么对局部变量不是这样? 问题答案: 为什么要对静态变量进行确定性初始化而对局部变量不进行初始化? 了解如何实现静态变量。 它们的内存在链接时分配,并且它们的初始值也在链接时提供。 没有运行时开销。 另一方面,用于局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前在那里。如果需要,可以清除该内存(将其清

  • 问题内容: 据我了解,“静态初始化块”用于设置静态字段的值(如果无法在一行中完成的话)。 但是我不明白为什么我们需要为此设置一个特殊的块。例如,我们将字段声明为静态(无值分配)。然后编写几行代码,生成并为上面声明的静态字段分配一个值。 为什么我们需要在这样一个特殊的块这样的行:? 问题答案: 在非静态块: 每次构造类的实例时被调用。在静态块只被调用一次,当类本身初始化,无论该类型的有多少对象创建。

  • 问题内容: 当我试图写这样的东西: 编译器无法编译它。 但是当我写这样的东西时: 并反编译后,我看到了静态初始化: 您能否向我解释这种行为? 问题答案: 您可以具有静态初始化,但不能具有静态块。静态初始化需要静态代码块来实现的事实确实改变了Java语法。 关键是您不打算在接口中包含代码(在Java 8之前),但是可以初始化字段。 顺便说一句,您可以拥有一个嵌套的类或枚举,该类或枚举具有您想要的尽可