快速入门JAVA单元测试——mock
背景
為了確保代碼的質量,對編寫的代碼進行單元測試是非常有必要的。
在JAVA項目中,一般的項目結構比較復雜、依賴眾多。在微服務與spring boot大行其道的今天,單純靠junit來進行單元測試一般很難完成對模塊的單元測試。
為了讓JAVA項目中的單元測試更加靈活便于編寫,各種mock框架應運而生,其中最為常用和經典的mock框架非mockito與powermock莫屬。
為了快速入門,本文將通過幾個實例讓大家快速了解mokito與powermock的使用方法。
同時將對mokito與powermock中幾個常見的疑問點通過幾個實例來解答疑惑。
示例場景
在一個龐大的項目中,有一個service和一個dao,現在想在對它們進行單元測試,僅驗證一下代碼中的業務邏輯代碼是否正確。
public interface UserService {String getUsernameById(int userId); }public class UserServiceImpl implements UserService {@Resourceprivate UserDao userDao;@Overridepublic String getUsernameById(int userId) {return userDao.getUsernameById(userId);} }public interface UserDao {String getUsernameById(int userId); }由于這兩個類一在spring容器中,通常由于網絡或環境原因,使用@SpringBootTest注解來將數據庫或整個項目全準備好一般也不太方便,這里可能就需要mock來解決了。
@mock注解
import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.MockitoAnnotations;import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when;public class UserServiceTest {@Mockprivate UserService userService;@Beforepublic void testInit() {MockitoAnnotations.initMocks(this);when(userService.getUsernameById(1)).thenReturn("1111111111");}@Testpublic void getUsernameByIdTest() {String username = userService.getUsernameById(1);System.out.println(username);}}輸出:
1111111111
從上面的代碼中可以看出,被@mock修飾后的變量userService則會產生一個userService的mock類,不用@mock注解用mock方法來產生一個userService是一樣的效果
@InjectMocks注解
import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations;import static org.mockito.Mockito.when;public class UserServiceTest {@Mockprivate UserDao userDao;@InjectMocksprivate UserServiceImpl userService;@Beforepublic void testInit() {MockitoAnnotations.initMocks(this);when(userDao.getUsernameById(2)).thenReturn("2222222222");}@Testpublic void getUsernameByIdTest() {String username = userService.getUsernameById(2);System.out.println(username);}}輸出:
2222222222
@InjectMocks注解修飾的類不能修飾抽象或接口類,被它修飾后,其被mock后的成員變量會注入到@InjectMocks的類中
比如上面的userDao則會被注入到userService中,當調用userService.getUsernameById方法時,原代碼里的userDao.getUsernameById方法則會執行mock方法
spy
@Testpublic void spyTest() {List<String> spyList = Mockito.spy(new ArrayList<>());when(spyList.size()).thenReturn(1000);spyList.add("abc");spyList.add("def");System.out.println(spyList.get(0));System.out.println(spyList.size());}輸出:
abc
1000
被spy修飾的方法默認會調用其真實的方法,如果有被mock限定了的條件才會先執行mock方法。
比如上面的例子,spyList這個list是被spy方法mock出來的,由于在mock聲明時只mock了它的size方法,所以調用它的get方法時還會執行原來的邏輯
靜態方法mock
有時在單元測試中會遇到需要對靜態方法也進行mock,比如想要對下面這個IpUtils工具類進行mock,那么mock注解就無能為力了。
import java.net.InetAddress; import java.net.UnknownHostException;public class IpUtils {public static String getIp() {try {return InetAddress.getLocalHost().getHostAddress();} catch (UnknownHostException e) {e.printStackTrace();}return null;} }mock靜態方法
一般對于需要將靜態方法進行mock可以借助于powermock來實現。
示例代碼如下:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner;import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic;@RunWith(PowerMockRunner.class) @PrepareForTest(IpUtils.class) public class UserServiceTest {@Beforepublic void init() {mockStatic(IpUtils.class);when(IpUtils.getIp()).thenReturn("192.168.1.123");}@Testpublic void testIp() {System.out.println(IpUtils.getIp());}}輸出:
192.168.1.123
使用時需要先用@PrepareForTest注解將需要mock的靜態類定義,之后再調用mockStatic方法進行mock即可。
上面的代碼測試時的依賴如下:
<dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.2</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.2</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>2.23.4</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito</artifactId><version>1.7.4</version><scope>test</scope></dependency>對樁模塊與驅動模塊概念的理解
在有了上面的示例代碼體驗后,再來理解一下什么是樁模塊,什么是驅動模塊
先看下百度百科上對樁模塊的解釋:
樁模塊(Stub)是指模擬被測試的模塊所調用的模塊,而不是軟件產品的組成的部分。主模塊作為驅動模塊,與之直接相連的模塊用樁模塊代替。在集成測試前要為被測模塊編制一些模擬其下級模塊功能的“替身”模塊,以代替被測模塊的接口,接受或傳遞被測模塊的數據,這些專供測試用的“假”模塊稱為被測模塊的樁模塊。
那么根據上面的定義,將樁模塊和驅動模塊與@Mock注解和@InjectMocks進行對應,那么被@Mock修飾的模塊則是樁模塊,被@InjectMocks修飾的模塊則是驅動模塊
總結
以上是生活随笔為你收集整理的快速入门JAVA单元测试——mock的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VUE前端框架介绍(基础)
- 下一篇: 扫描图像自动校正的实现