从测试方法访问时单例返回新实例
Singleton returning new instance when accessed from test method
我正在使用 Junit 4.12 和 PowerMock 1.6 以及 Mockito。我还使用了 PowerMockRule 库,如 here. I am trying to execute initialization code for all of my test cases exactly once as described in 所述。但是,它只执行一次初始化代码,如果我在测试方法中执行 ServiceInitializer.INSTANCE
它 returns 我的新对象。我无法理解这种行为。有谁知道为什么会这样?如果我在没有 PowerMockRule 库的情况下执行我的代码并且 运行 我使用 PowerMockRunner 进行测试那么它工作正常但在那种情况下我的 ClassRule 没有被执行。
public class ServiceInitializer extends ExternalResource {
public static final TestRule INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
@Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this); //Print Address @3702c2f1
}
@Override protected void after() {
}
}
class BaseTest{
@Rule
public PowerMockRule powerMockRule = new PowerMockRule();
@ClassRule
public static final TestRule serviceInitializer = ServiceInitializer.INSTANCE;
@Before
public final void preTest() {
// some code
}
@After
public final void postTest() {
//some code
}
}
@PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
@Test
public void testMethodA_1(){
System.out.println(ServiceInitializer.INSTANCE);//Print Address @54d41c2b
}
}
更新
我打印了 类 的类加载器,结果第一个打印语句的类加载器是 sun.misc.Launcher$AppClassLoader
,第二个打印语句的类加载器是 org.powermock.core.classloader.MockClassLoader
。我该如何解决这个问题?
您没有单身人士。您有一个静态 INSTANCE 变量。请记住,您拥有的每个类加载器都可以存在其中之一。
而是创建一个 ServiceInitializer 的枚举,就像这样
public enum ServiceInitializer {
INSTANCE;
// rest of class goes here
}
并且依靠JVM的语言契约来保证单例。
或者,更好的是,编写代码来处理可以存在多个 ServiceInitializer 的情况,但碰巧您的程序只使用一个实例。这是理想的选择,允许您根据需要在真正的 ServiceInitializer 和模拟之间切换。
埃德温是正确的;这是 PowerMock 为每个测试创建一个新的 ClassLoader 的问题。我强烈建议重构您的代码,以便在没有 PoeerMock 的情况下进行测试并切换到 Mockito。
这些书可能会有帮助
同时,您可以参考 ServiceInitializer
来自您的基地 class:
public class ServiceInitializer extends ExternalResource {
public static final ServiceInitializer INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
@Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this);
}
@Override protected void after() {
}
}
class BaseTest{
@Rule
public PowerMockRule powerMockRule = new PowerMockRule();
@ClassRule
public static final ServiceInitializer serviceInitializer = ServiceInitializer.INSTANCE;
@Before
public final void preTest() {
// some code
}
@After
public final void postTest() {
//some code
}
}
@PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
@Test
public void testMethodA_1(){
System.out.println(serviceInitializer);
}
}
好吧,我终于找到了解决这个问题的方法。正如我在问题中所解释的那样,我的 class 被两个不同的 class 加载程序加载,因此给我带来了问题。为了解决我的问题,我使用 @PowerMockIgnore
注释来延迟其加载,如下所示:
@PowerMockIgnore({"com.mypackage.*"})
class BaseTest{
// Stuff goes here
}
这个注解告诉 PowerMock 延迟 classes 的加载,这些名字带有提供给 value() 的名称到系统 classloader。您可以从 here.
中了解此注释
我正在使用 Junit 4.12 和 PowerMock 1.6 以及 Mockito。我还使用了 PowerMockRule 库,如 here. I am trying to execute initialization code for all of my test cases exactly once as described in ServiceInitializer.INSTANCE
它 returns 我的新对象。我无法理解这种行为。有谁知道为什么会这样?如果我在没有 PowerMockRule 库的情况下执行我的代码并且 运行 我使用 PowerMockRunner 进行测试那么它工作正常但在那种情况下我的 ClassRule 没有被执行。
public class ServiceInitializer extends ExternalResource {
public static final TestRule INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
@Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this); //Print Address @3702c2f1
}
@Override protected void after() {
}
}
class BaseTest{
@Rule
public PowerMockRule powerMockRule = new PowerMockRule();
@ClassRule
public static final TestRule serviceInitializer = ServiceInitializer.INSTANCE;
@Before
public final void preTest() {
// some code
}
@After
public final void postTest() {
//some code
}
}
@PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
@Test
public void testMethodA_1(){
System.out.println(ServiceInitializer.INSTANCE);//Print Address @54d41c2b
}
}
更新
我打印了 类 的类加载器,结果第一个打印语句的类加载器是 sun.misc.Launcher$AppClassLoader
,第二个打印语句的类加载器是 org.powermock.core.classloader.MockClassLoader
。我该如何解决这个问题?
您没有单身人士。您有一个静态 INSTANCE 变量。请记住,您拥有的每个类加载器都可以存在其中之一。
而是创建一个 ServiceInitializer 的枚举,就像这样
public enum ServiceInitializer {
INSTANCE;
// rest of class goes here
}
并且依靠JVM的语言契约来保证单例。
或者,更好的是,编写代码来处理可以存在多个 ServiceInitializer 的情况,但碰巧您的程序只使用一个实例。这是理想的选择,允许您根据需要在真正的 ServiceInitializer 和模拟之间切换。
埃德温是正确的;这是 PowerMock 为每个测试创建一个新的 ClassLoader 的问题。我强烈建议重构您的代码,以便在没有 PoeerMock 的情况下进行测试并切换到 Mockito。
这些书可能会有帮助
同时,您可以参考 ServiceInitializer
来自您的基地 class:
public class ServiceInitializer extends ExternalResource {
public static final ServiceInitializer INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
@Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this);
}
@Override protected void after() {
}
}
class BaseTest{
@Rule
public PowerMockRule powerMockRule = new PowerMockRule();
@ClassRule
public static final ServiceInitializer serviceInitializer = ServiceInitializer.INSTANCE;
@Before
public final void preTest() {
// some code
}
@After
public final void postTest() {
//some code
}
}
@PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
@Test
public void testMethodA_1(){
System.out.println(serviceInitializer);
}
}
好吧,我终于找到了解决这个问题的方法。正如我在问题中所解释的那样,我的 class 被两个不同的 class 加载程序加载,因此给我带来了问题。为了解决我的问题,我使用 @PowerMockIgnore
注释来延迟其加载,如下所示:
@PowerMockIgnore({"com.mypackage.*"})
class BaseTest{
// Stuff goes here
}
这个注解告诉 PowerMock 延迟 classes 的加载,这些名字带有提供给 value() 的名称到系统 classloader。您可以从 here.
中了解此注释