Java_UT_Mock系列之-04Powermock与单例模式

测试场景

单例模式是常见的一种创建型设计模式,保证了采用该模式的类的实例的全局唯一性。但对于UT来说,由于其屏蔽了类的创建过程,其testability是有待商榷的。
如以下案例,

public class ClassToUseSingleton {
    public String invokeSingleton()
    {
        return Singleton.getInstance().printHelloWorld( "Hi!!!" );
    }
}

上述被测应用中的invokeSingleton方法调用了一个Singleton单例类的方法来完成某项特定工作。该单例类的源码如下:

public class Singleton
{
    public String printHelloWorld( String value )
    {
        StringBuilder stringBuilder
            = new StringBuilder( "The string value is: " );
        return stringBuilder.append( value ).toString();
    }

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

Mock实现

通过观察上述代码,可以发现mock的难点在于

  1. 私有内部类
    该单例模式采取了内部类的方式SingletonInstance来持有一个私有且final的Singleton 对象实例,这样就保证了Singleton实例的全局唯一性,并且是线程安全的。
    private static final Singleton INSTANCE
  2. 静态方法/变量
    getInstance()是一个静态方法,常用的通过new的方式来注入一个mock对象的方法不能使用。
    而通过Powermock,则可以解决上述问题。主要思路是,当调用getInstance()方法时,返回一个被mock过的Singleton 实例来替换对SingletonInstance.INSTANCE的调用。
    示例代码如下
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import static org.junit.Assert.assertEquals;
@RunWith( PowerMockRunner.class )
@PrepareForTest(Singleton.class )
public class ClassToUseSingletonTest
{
    @Test
    public void testSingeton() throws Exception {
    Singleton mockSingleton =  PowerMockito.mock(Singleton.class);
        Class clazz = Whitebox.getInnerClassType(Singleton.class, "SingletonInstance");
        Whitebox.setInternalState(clazz, "INSTANCE", mockSingleton);
        PowerMockito.when( mockSingleton.printHelloWorld( Mockito.anyString() ) )
                    .thenReturn( "Mocked!!" );
        assertEquals( "Mocked!!",
                      new ClassToUseSingleton().invokeSingleton() );
    }
}

案例分析

这里主要使用了Whitebox这个工具,

Class clazz = Whitebox.getInnerClassType (Singleton.class, "SingletonInstance");

通过这行代码,获取到了内部类SingletonInstance。
然后,再将mockSingleton赋给内部私有变量 "INSTANCE",

Whitebox.setInternalState(clazz, "INSTANCE", mockSingleton);

这样,就实现了当调用SingletonInstance.INSTANCE时,将返回被mock过的Singleton对象mockSingleton ,也就是实现了对于单例模式的模拟。

你可能感兴趣的:(Java_UT_Mock系列之-04Powermock与单例模式)