1. 程式人生 > >單元測試時靜態方法注意點

單元測試時靜態方法注意點

Mockito 很強大, 但是它不支援靜態方法.所以, 就用Powermock了. 要測試的物件是Controller中的介面, 對單元測試來說, 這個待測試的街口應該是一個白盒的. 所以, 其中的第三方(service,其他靜態類)的呼叫都應該使用mock物件來stub起來. 下面是## 示例程式碼(隨手寫的,說明問題就行):

Controller:  

@Controllerpublic class MainController {

    @Autowired    private MainService mainService;

    @GetMapping("/hello")
    public String hello(){
        mainService.hello();
        String result = HelloUtil.hi();
        if(result.equals("hi")){
            return "ok";
        }else {
            return "fail";
        }

    }
}

Service:

/**
 * Created by icer on 2017/10/13.
 */@Service public class MainService {

    public String hello(){
        System.out.println("hello");
        return "hello";
    }
}

Util靜態類:  

```javapublic class HelloUtil {

    public static String hi(){
        return "hi";
    }
}

我們要測試的是MainController中的hello方法.

再次注意, 是hello方法,不是hello介面.  我們的測試類:

@RunWith(PowerMockRunner.class)@PrepareForTest(HelloUtil.class)public class MainControllerTest {

    [@Mock](https://my.oschina.net/mock)    private MainService mainService;

    @InjectMocks    private MainController mainController;

    [@Test](https://my.oschina.net/azibug)    public void helloTest(){
        PowerMockito.when(mainService.hello()).thenReturn("hello");
        PowerMockito.mockStatic(HelloUtil.class);
        PowerMockito.when(HelloUtil.hi()).thenReturn("hi");
        HelloUtil.hi();
        HelloUtil.hi();
        String result = mainController.hello();
        PowerMockito.verifyStatic(Mockito.times(3));
        HelloUtil.hi();
        HelloUtil.hi();
        HelloUtil.hi();
     // mainController.hello();        assertEquals(result, "ok");
    }
}

我們著重看的是上面的HelloUtil.hi()方法.

PowerMockito.verifyStatic(Mockito.times(3));

先說下這句的意思, 這句(後面簡稱靜態校驗)就是對靜態方法的呼叫次數做了校驗. 光看命名就能想通. 但是要注意的是, 這裡的3指的是在靜態校驗之前的呼叫次數. 如果靜態校驗執行的次數不等於這個次數, 那麼靜態校驗後面的靜態方法就不能再執行了. 上面程式碼中, 在這句前一共執行了三次HelloUtil.hi(); 兩次是主動明文執行的, 一次是在mainController.hello()裡面執行的. 如果註釋掉某一個, 那麼就會看到日誌列印:

org.mockito.exceptions.verification.TooLittleActualInvocations: cn.com.hanbinit.utils.HelloUtil.hi();Wanted 3 times but was 2 times.

這裡只校驗靜態校驗之前的執行次數, 後面執行多少次都沒關係. 另外, 還有一點要注意的是, 在校驗語句前, 我們前面給方法mock的返回值是有效的, 在校驗之後就沒效果了. 
所以, 我們對靜態方法的測試, 應該是放在verifyStatis之前進行的.  在校驗之後的執行, 是為了證明我們之前的校驗是ok的.

校驗之後的靜態方法呼叫, 是一定要有的. 要不然是測試不出呼叫次數的. 參見下面程式碼:

@Test    public void helloTest(){
        PowerMockito.when(mainService.hello()).thenReturn("hello");
        PowerMockito.mockStatic(HelloUtil.class);
        PowerMockito.when(HelloUtil.hi()).thenReturn("hi");
        String result = HelloUtil.hi();
        String hi = HelloUtil.hi();
        PowerMockito.verifyStatic(Mockito.times(4));
        assertEquals(result, "hi");
    }

像上面這段程式碼, 測試是通過的.  改成下面這樣:

@Test    public void helloTest(){
        PowerMockito.when(mainService.hello()).thenReturn("hello");
        PowerMockito.mockStatic(HelloUtil.class);
        PowerMockito.when(HelloUtil.hi()).thenReturn("hi");
        String result = HelloUtil.hi();
        String hi = HelloUtil.hi();
        PowerMockito.verifyStatic(Mockito.times(4));
        HelloUtil.hi();
        assertEquals(result, "hi");
    }

就能看到日誌:

org.mockito.exceptions.verification.TooLittleActualInvocations: cn.com.hanbinit.utils.HelloUtil.hi();Wanted 4 times but was 2 times.

所以, 對靜態方法呼叫次數的校驗一定是verifyStatis和之後的顯示呼叫一起來進行的. 最後的最後, 再提一句:  如果同時有多個靜態方法呼叫. 如果都要驗證呼叫次數, 那麼應該分別像下面這樣使用多次:

PowerMockito.verifyStatic(Mockito.times(4));
HelloUtil.hi();