2016年12月8日 星期四

Testing: 隔離框架(Isolation framework) - 使用PowerMock驗證靜態方法

前言

Testing: 隔離框架(Isolation framework) - 使用Mockito建立測試替身一文中,我們練習如何使用Mockito框架模擬測試替身後,接著要來學習PowerMock的框架,輔助我們測試與驗證靜態方法。

PowerMock的使用

PowerMock使用起來相當簡單,首先我們在gradle中引用PowerMock,程式碼如下。

dependencies {
    testCompile 'org.powermock:powermock-module-junit4:1.6.2'
    testCompile 'org.powermock:powermock-api-mockito:1.6.2'
}

以RegisterManager為例

  • RegisterManager物件
public class RegisterManager {
    private IEmailRegister emailRegister;
    private IMobileRegister mobileRegister;

    public RegisterManager(IEmailRegister emailRegister, IMobileRegister mobileRegister) {
        this.emailRegister = emailRegister;
        this.mobileRegister = mobileRegister;
    }

    public void register(String content) {
        if (RegistrationFormat.checkFormat(content)) {
            this.emailRegister.emailRegister();
        } else {
            this.mobileRegister.mobileRegister();
        }
    }
}
  • RegistrationFormat類別
public class RegistrationFormat {

    public static boolean checkFormat(String content) {
        boolean isEmail = false;

        if (content.contains("@")) {
            return isEmail;
        }

        return !isEmail;
    }
}
  • IEmailRegister介面
public interface IEmailRegister {
    void emailRegister();
}
  • IMobileRegister介面
public interface IMobileRegister {
    void mobileRegister();
}

RegisterManager的功能相當簡單,它提供了Email與Mobile這兩種註冊方法,透過RegistrationFormat類別的靜態方法檢查,若是屬於Email格式,就使用Email註冊,Mobile註冊則反之,接著我們來看測試程式。

@RunWith(PowerMockRunner.class)
@PrepareForTest(RegistrationFormat.class)
public class RegisterManagerWithPowerMockitoTest {

    @Test
    public void testRegisterIsEmail() throws Exception {
        // Arrange
        IEmailRegister mockEmailRegister = Mockito.mock(IEmailRegister.class);
        IMobileRegister mockMobileRegister = Mockito.mock(IMobileRegister.class);
        PowerMockito.mockStatic(RegistrationFormat.class);
        Mockito.when(RegistrationFormat.checkFormat(Mockito.anyString())).thenReturn(true);
        RegisterManager registerManager = new RegisterManager(mockEmailRegister, mockMobileRegister);

        // Act
        registerManager.register(Mockito.anyString());

        // Assert
        Mockito.verify(mockEmailRegister).emailRegister();
    }

    @Test
    public void testRegisterIsMobile() throws Exception {
        // Arrange
        IEmailRegister mockEmailRegister = Mockito.mock(IEmailRegister.class);
        IMobileRegister mockMobileRegister = Mockito.mock(IMobileRegister.class);
        PowerMockito.mockStatic(RegistrationFormat.class);
        Mockito.when(RegistrationFormat.checkFormat(Mockito.anyString())).thenReturn(false);
        RegisterManager registerManager = new RegisterManager(mockEmailRegister, mockMobileRegister);

        // Act
        registerManager.register(Mockito.anyString());

        // Assert
        Mockito.verify(mockMobileRegister).mobileRegister();
    }

    @Test
    public void testRegistrationFormatCheckFormatIsCalled() throws Exception {
        // Arrange
        IEmailRegister mockEmailRegister = Mockito.mock(IEmailRegister.class);
        IMobileRegister mockMobileRegister = Mockito.mock(IMobileRegister.class);
        PowerMockito.mockStatic(RegistrationFormat.class);
        Mockito.when(RegistrationFormat.checkFormat(Mockito.anyString())).thenReturn(false);
        RegisterManager registerManager = new RegisterManager(mockEmailRegister, mockMobileRegister);

        // Act
        registerManager.register(Mockito.anyString());

        // Assert
        PowerMockito.verifyStatic(Mockito.times(1));
        RegistrationFormat.checkFormat(Mockito.anyString());
    }
}

要使用PowerMock首先要依循以下方式:
  • @RunWith(PowerMockRunner.class): 使用@RunWith的Annotation,並且類別指定PowerMockRunner.class。
  • @PrepareForTest(RegistrationFormat.class): 使用@PrepareForTest的Annotation,聲明要Mock的靜態方法類別,這邊指定RegistrationFormat.class。
  • PowerMockito.mockStatic(RegistrationFormat.class): 使用mockStatic方法Mock靜態類別

接著預期我們的測試程式,可以使用下列方法。
  • Mockito.when(RegistrationFormat.checkFormat(Mockito.anyString())).thenReturn(false): 如同Mockito的方法,我們指定RegistrationFormat使用checkFormat的靜態方法時,所欲回傳的值。
  • PowerMockito.verifyStatic(Mockito.times(1)): 先斷言該靜態方法執行的次數。
  • RegistrationFormat.checkFormat(Mockito.anyString()): 實際執行該方法。

這邊需要注意的是,PowerMock斷言的方式有些奇怪,要先斷言再執行呼叫,值得注意喔!這邊也能順利通過測試,如下圖。


小結

我們都順利討論完隔離框架的使用啦!但目前為止,這些議題是個人學習單元測試上,覺得非常重要的觀念,希望對大家都有幫助,之後也會不定期更新相關議題,若有任何意見,也歡迎提供給我,謝謝大家!

Source Code

Github: https://github.com/xavier0507/UnitTestSample.git

沒有留言:

張貼留言