什么是單元測(cè)試
單元測(cè)試是由一組獨(dú)立的測(cè)試構(gòu)成腾供,每個(gè)測(cè)試針對(duì)軟件中的一個(gè)單獨(dú)的程序單元疹启。單元測(cè)試并非檢查程序單元直接是否能夠合作良好,而是檢查單個(gè)程序單元的行為是否正確玻驻。
事實(shí)上曙聂,單元測(cè)試是一種驗(yàn)證行為,測(cè)試和驗(yàn)證程序中的每一項(xiàng)的正確性苦囱。
為什么要進(jìn)行單元測(cè)試
對(duì)于單元測(cè)試嗅绸,人們往往存在很多誤解:
- 浪費(fèi)時(shí)間太多,本身項(xiàng)目的時(shí)間就很緊張撕彤,沒(méi)有時(shí)間去寫(xiě)單元測(cè)試鱼鸠。
- 過(guò)度的依賴測(cè)試人員,認(rèn)為軟件開(kāi)發(fā)人員不應(yīng)該參與單元測(cè)試羹铅。
- 認(rèn)為單元測(cè)試不必要瞧柔,代碼寫(xiě)得很好了,no bug睦裳,no warning造锅。
- 老代碼結(jié)構(gòu)混亂,耦合度高廉邑,為了寫(xiě)單元測(cè)試修改代碼結(jié)構(gòu)哥蔚,意義不大倒谷,投入跟產(chǎn)出不成比例。
單元測(cè)試真的這么雞肋么糙箍?No渤愁,No,NoI詈弧6陡瘛!
試想
測(cè)試人員給你報(bào)了一個(gè)bug咕晋,但是由于之后的merge失誤導(dǎo)致代碼丟失雹拄,或者別人修改代碼導(dǎo)致這個(gè)bug再次復(fù)現(xiàn)。
重構(gòu)代碼的時(shí)候掌呜,被bug淹沒(méi)滓玖。造成你持續(xù)不斷的改bug,持續(xù)不斷的加班质蕉。
-
明明很正常的功能势篡,怎么現(xiàn)在突然不能用了?是接口的問(wèn)題模暗,還是有人修改了這個(gè)功能的邏輯禁悠?
。兑宇。碍侦。
如果你也經(jīng)常遇到這些困惑,那么你就需要對(duì)項(xiàng)目進(jìn)行單元測(cè)試了顾孽。
因?yàn)?strong>單元測(cè)試具有以下優(yōu)勢(shì):
-
幫助理解需求
單元測(cè)試應(yīng)該反映Use Case,把被測(cè)單元當(dāng)成黑盒測(cè)試其外部行為比规。
-
提高實(shí)現(xiàn)質(zhì)量
單元測(cè)試不保證程序做正確的事若厚,但能幫助保證程序正確地做事,從而提高實(shí)現(xiàn)質(zhì)量蜒什。
-
測(cè)試成本低
相比集成測(cè)試测秸、驗(yàn)收測(cè)試,單元測(cè)試所依賴的外部環(huán)境少灾常,自動(dòng)化程度高霎冯,時(shí)間短,節(jié)約了測(cè)試成本钞瀑。
-
反饋速度快
單元測(cè)試提供快速反饋沈撞,把bug消滅在開(kāi)發(fā)階段,減少問(wèn)題流到集成測(cè)試雕什、驗(yàn)收測(cè)試和用戶缠俺,降低了軟件質(zhì)量控制的成本显晶。
-
利于重構(gòu)
由于有單元測(cè)試作為回歸測(cè)試用例,有助于預(yù)防在重構(gòu)過(guò)程中引入bug壹士。
-
文檔作用
單元測(cè)試提供了被測(cè)單元的使用場(chǎng)景磷雇,起到了使用文檔的作用。
-
對(duì)設(shè)計(jì)的反饋
一個(gè)模塊很難進(jìn)行單元測(cè)試通常是不良設(shè)計(jì)的信號(hào)躏救,單元測(cè)試可以反過(guò)來(lái)指導(dǎo)設(shè)計(jì)出高內(nèi)聚唯笙、低耦合的模塊。
怎么進(jìn)行單元測(cè)試
Android 單元測(cè)試分類
Android 單元測(cè)試分為兩大類:
app/src
├── androidTestjava (Instrumented 單元測(cè)試崩掘、UI測(cè)試)
├── main/java (業(yè)務(wù)代碼)
└── test/java (Local 單元測(cè)試)
-
Local test:
運(yùn)行在本地的JVM虛擬機(jī)上,不依賴Android框架忠怖。
-
Instrumented tests:
通過(guò)Android系統(tǒng)的Instrumented測(cè)試框架呢堰,運(yùn)行測(cè)試代碼在真實(shí)手機(jī)上。
Android Junit + Mockito + Powermock單元測(cè)試方案
Junit + Mockito + Powermock簡(jiǎn)介
Junit 是一個(gè)Java語(yǔ)言的單元測(cè)試框架
Mockito 是一個(gè)Mock框架凡泣,我們可以通過(guò)Mockito框架創(chuàng)建配置mock對(duì)象枉疼。
Powermock 可以針對(duì)static,final鞋拟,private方法進(jìn)行mock
Junit + Mockito + Powermock使用
強(qiáng)烈建議你熟讀以下內(nèi)容骂维,來(lái)熟悉Junit + Mockito + Powermock的使用。
- Mockito 中文文檔 ( 2.0.26 beta )
- Mockito reference documentation
- powermock wiki
- Unit tests with Mockito - Tutorial
比如說(shuō)我們要對(duì)Calculate類進(jìn)行單元測(cè)試
public class Calculate {
private int mPrivate;
private final int mPrivateFinal = 0;
private static int mPrivateStatic = 0;
private static final int mPrivateStaticFinal = 0;
public int mPublic;
public final int mPublicFinal = 0;
public static int mPublicStatic = 0;
public static final int mPublicStaticFinal = 0;
public void voidPublicMethod(int a, int b) {
return;
}
public int addPublicMethod(int a, int b) {
return a + b;
}
private int addPrivateMethod(int a, int b) {
return a + b;
}
public static int addPublicStaticMethod(int a, int b) {
return a + b;
}
private static int addPrivateStaticMethod(int a, int b) {
return a + b;
}
}
-
測(cè)試Public 變量
@Test public void testPublicField() { assertEquals(mCalculate.mPublic, 0); assertEquals(mCalculate.mPublicFinal, 0); assertEquals(Calculate.mPublicStatic, 0); assertEquals(Calculate.mPublicStaticFinal, 0); mCalculate.mPublic = 1; Calculate.mPublicStatic = 2; assertEquals(mCalculate.mPublic, 1); assertEquals(mCalculate.mPublicFinal, 0); assertEquals(Calculate.mPublicStatic, 2); }
-
測(cè)試Public 方法
@Test public void testAddPublicMethod() { //when when(mCalculate.addPublicMethod(anyInt(), anyInt())) .thenReturn(0) .thenReturn(1); //call method for (int i = 0; i < 2; i++) { //verify assertEquals(mCalculate.addPublicMethod(i, i), i); } //verify verify(mCalculate, times(2)).addPublicMethod(anyInt(), anyInt()); verify(mCalculate, atLeast(1)).addPublicMethod(anyInt(), anyInt()); verify(mCalculate, atLeastOnce()).addPublicMethod(anyInt(), anyInt()); verify(mCalculate, atMost(2)).addPublicMethod(anyInt(), anyInt()); }
-
測(cè)試Public 返回Void 方法
@Test public void testAddPublicVoidMethod() { //when doNothing().when(mCalculate).voidPublicMethod(anyInt(), anyInt()); }
-
測(cè)試Public Static 方法
@Test public void testAddPublicStaicMethod() throws Exception { PowerMockito.mockStatic(Calculate.class); PowerMockito.when(Calculate.class, "addPublicStaticMethod", anyInt(), anyInt()) .thenReturn(0) .thenReturn(1); }
-
測(cè)試Private Static 變量
@Test public void testPrivate() throws IllegalAccessException { PowerMockito.mockStatic(Calculate.class); assertEquals(Whitebox.getField(Calculate.class, "mPrivate").getInt(mCalculate), 0); assertEquals(Whitebox.getField(Calculate.class, "mPrivateFinal").getInt(mCalculate), 0); assertEquals(Whitebox.getField(Calculate.class, "mPrivateStatic").getInt(null), 0); assertEquals(Whitebox.getField(Calculate.class, "mPrivateStaticFinal").getInt(null), 0); Whitebox.setInternalState(mCalculate, "mPrivate", 1); Whitebox.setInternalState(Calculate.class, "mPrivateStatic", 1, Calculate.class); assertEquals(Whitebox.getField(Calculate.class, "mPrivate").getInt(mCalculate), 1); assertEquals(Whitebox.getField(Calculate.class, "mPrivateFinal").getInt(mCalculate), 0); assertEquals(Whitebox.getField(Calculate.class, "mPrivateStatic").getInt(null), 1); assertEquals(Whitebox.getField(Calculate.class, "mPrivateStaticFinal").getInt(null), 0); }
-
測(cè)試Private 方法
@Test public void testAddPrivateMethod() throws Exception { PowerMockito.mockStatic(Calc.class); //when PowerMockito.when(mCalculate,"addPrivateMethod",anyInt(),anyInt()) .thenReturn(0) .thenReturn(1); }
-
測(cè)試Private static 方法
@Test public void testAddPrivateStaicMethod() throws Exception { PowerMockito.mockStatic(Calculate.class); PowerMockito.when(Calculate.class, "addPrivateStaticMethod", anyInt(), anyInt()) .thenReturn(0) .thenReturn(1); }
對(duì)單例進(jìn)行mock
比如對(duì)以下代碼進(jìn)行mock
public class Singleton {
public String PUBLIC_STR = "public_str";
private static class ServiceSingleton {
private static final Singleton SINGLE = new Singleton();
}
public static Singleton get() {
return ServiceSingleton.SINGLE;
}
public String getPublicStr() {
return PUBLIC_STR;
}
}
public class SingletonUse {
private static final String PUBLIC_STR = "public_str";
private static class ServiceSingleton {
private static final SingletonUse SINGLE = new SingletonUse();
}
public static SingletonUse get() {
return ServiceSingleton.SINGLE;
}
public String getUsePublicStr() {
return Singleton.get().getPublicStr();
}
}
測(cè)試類
@RunWith(PowerMockRunner.class)
@PrepareForTest({Singleton.class, SingletonUse.class})
public class SingletonUseTest {
private SingletonUse singletonUse;
@Before
public void setUp() throws Exception {
Singleton singleton = PowerMockito.mock(Singleton.class);
PowerMockito.mockStatic(Singleton.class);
PowerMockito.doReturn(singleton).when(Singleton.class, "get");
singletonUse = PowerMockito.mock(SingletonUse.class);
PowerMockito.mockStatic(SingletonUse.class);
PowerMockito.doReturn(singletonUse).when(SingletonUse.class, "get");
}
@Test
public void getUsePublicStr() throws Exception {
PowerMockito.doReturn("123456").when(singletonUse,"getUsePublicStr");
}
}