測試 之Java單元測試、Android單元測試
我的目的,旨在介紹一個不一樣的“單元測試” !
其實對於單元測試這一塊,我很早已經開始關注了,也蒐羅了好多這方面技術的部落格。要麼只有文字性的,要麼都是程式碼的邏輯類,籠統、沒有什麼脈絡可循。之後依然半解,放到專案中,用起來還是不方便,就是覺得這樣比我直接執行在模擬器(真機)之後的過程列印、除錯要慢好多!
因此,就導致後來的放棄。以及今天的再次拾起,並做一個系統點的介紹,希望特別想要使用單元測試的朋友能夠用得著。
不一樣 、不一樣的單元測試
上面,我擷取了一個專案中module的目錄結構圖。看完之後,映入眼簾的是紅色框中那兩個括弧
中的內容
androidTest
test
她們倆是做什麼的?我們就只從JUit出發,進行詳細無死角的介紹
JUnit 測試
一般而言,使用Android Studio
做安卓開發的我們對專案做測試,無論專案搭建的準備工作或是專案開發進行中的功能性測試。都無非用到兩種環境下的測試:
test | androidTest |
---|---|
Java環境下的測試 | Android環境下的測試 |
區別
環境配置上的區別
module下的build.gradle檔案中那行行程式碼
1,支援能夠Java虛擬機器裝置環境下的預設環境配置;
/** build.gradle/dependencies{}中 **/
testCompile 'junit:junit:4.12'
2,支援能夠Android裝置環境下的預設環境配置;
/** build.gradle/dependencies{}中 **/
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
/** 且android/defaultConfig{}節點要加上 **/
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
程式碼類配置的區別
她們之間有明眼的區別,class註解上 的@RunWith(AndroidJUnit4.class)
即可看到,IDE在兩個包內也為我們生成的測試程式碼類
在Android裝置上執行的範例(androidTest)包
下 && 在Java虛擬機器裝置上執行的範例(test)包
下
好的,我們該如何使用呢?這是個關鍵問題!接下來看兩種使用方式
test包下的Java環境下的測試
針對的是安卓專案。在基於MVP架構的基礎上,使用OkHttp
作網路資料請求,並對其做簡單封裝,以使用見最簡單方式來實現與後臺的資料互動。
測試目的:測試get和post兩種請求方式是否成功
構建測試方式一 在要測試的類中 右擊滑鼠/Go To/Test/Create New Test..
之後,你會發現新彈出的頁面
Testing library | Class | Superclass | Destination Package | Generate | Generate test methods for | member |
---|---|---|---|---|---|---|
JUnit4 | 測試的類名 | 測試類的父類名 | 包名 | 勾選 | 不勾選 | 選擇性勾選 |
隨便展示下圖片效果,如圖<圖片很隨便,內容真誠>
點選確定
進入新的頁面框,走入目錄
當然是選擇java虛擬機器裝置下的測試方式哦!
然後測試類則生成,但是需要注意的是。你使用了這種方式並不會一定會生成你想要的效果。為什麼?
答案很簡單,使用單元測試,測試的程式碼邏輯塊必須是獨立的。
拿此例來說,使用okhttp的post方法做測試是否與後臺能聯通?那麼,就必須讓測試的程式碼邏輯塊與無關的類解耦。比如在測試方法中以簡潔的程式碼邏輯塊 實現。或者,寫一個管理類能直接管理post呼叫的操作。
好,我上程式碼表示,以簡潔的程式碼邏輯塊 實現post請求
/**
* 功能:使用post方式進行http請求的測試
*/
@Test
public void post() {
MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String url = "http://httpbin.org/post";
RequestBody body = RequestBody.create(JSON, "{\"name\":\"xueshuyuan\"}");
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
System.out.println("輸出get方式請求的結果:==>>" + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
當然也能使用一個類先對上面的程式碼邏輯塊 封裝,然後在測試中一行程式碼邏輯搞定。
一般的測試結構,就是生成了這樣的效果。這種結構基本是固定的,所以自己完全可以在(test)包中手動建立。
public class xxxTest {
...
/**
* 功能:在get()/post()方法執行之前優先執行
* @throws Exception
*/
@Before
public void setUp() throws Exception {
..
}
@Test
public void post() throws Exception {
..
}
@Test
public void XXMethod() throws Exception {
..
}
/**
* 功能:在加註解@Before的方法setUp()執行之後立即執行
* @throws Exception
*/
@after
public void TearDown() throws Exception {
..
}
}
其中,上標註了@Before
@after
方法名是固定的,並且執行順序也是固定的。
比如對方法post進行測試,右擊滑鼠,選中執行Run 'post()'
列印結果:
如果我換一個單元測試類,這時用到了Rxjava進行了執行緒的排程,但依然是基於Java環境的單元測試
/**
* @Title:RxjavaTestInJava
* @Auther:YJH
* @Email:yuannunhua@gmail.com
* @Date:2018/6/23 20:13
*/
public class RxjavaTestInJava {
@Before
public void setUp() throws Exception {
Thread.currentThread().setName("currentThread");
}
@Test
public void schedulerTest() {
//觀察者(訂閱者)
final Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
System.out.println("onCompleted=" + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
System.out.println("onError=" + Thread.currentThread().getName());
e.printStackTrace();
}
@Override
public void onNext(String result) {
System.out.println("onNext=" + Thread.currentThread().getName());
System.out.println("onNext=" + result);
}
};
//被觀察者
final Observable observable = Observable.create(new Observable.OnSubscribe<Subscriber>() {
@Override
public void call(Subscriber subscriber1) {
System.out.println("Observable-call=" + Thread.currentThread().getName());
subscriber1.onStart();
subscriber1.onNext("hello world");
subscriber1.onCompleted();
}
});
observable.subscribeOn(Schedulers.io()) //指生產事件在當前的執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) //指消費事件在主執行緒中進行
.subscribe(subscriber);
}
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
接下來你會看到報錯、報錯
但如果,我把第56行程式碼
換掉,換作.observeOn(Schedulers.newThread())
結果又會不同且正常執行。那是為什麼呢?
androidTest 包下的Android環境下的測試
原因是,在第56行程式碼
使用了Android的API,已經不是能在Java環境下正常執行的了。所以為了執行該程式碼邏輯塊,看看是否是我們想要的邏輯程式碼。這時就需要執行在Android環境下。即在(androidTest)包
下新建一個同樣的類邏輯,只做些許的必要修改 (改變參照兩種測試環境的不同而做改變)
1,類名上新增註解 @RunWith(AndroidJUnit4.class)
2,增加一些Android平臺的註釋 Log.e 以做日誌列印
/**
* @Title:RxjavaTestInJava
* @Auther:YJH
* @Email:yuannunhua@gmail.com
* @Date:2018/6/23 20:13
*/
@RunWith(AndroidJUnit4.class)
public class RxjavaTestInJava {
@Before
public void setUp() throws Exception {
Thread.currentThread().setName("currentThread");
}
@Test
public void schedulerTest() {
final String tag = "test";
//觀察者(訂閱者)
final Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
System.out.println("onCompleted=" + Thread.currentThread().getName());
Log.e(tag, "onCompleted=" + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
System.out.println("onError=" + Thread.currentThread().getName());
e.printStackTrace();
}
@Override
public void onNext(String result) {
System.out.println("onNext=" + Thread.currentThread().getName());
System.out.println("onNext=" + result);
Log.e(tag, "onNext=" + result);
}
};
//被觀察者
final Observable observable = Observable.create(new Observable.OnSubscribe<Subscriber>() {
@Override
public void call(Subscriber subscriber1) {
System.out.println("Observable-call=" + Thread.currentThread().getName());
Log.e(tag, "Observable-call=" + Thread.currentThread().getName());
subscriber1.onStart();
subscriber1.onNext("hello world");
Log.e(tag, "hello world");
subscriber1.onCompleted();
}
});
observable.subscribeOn(Schedulers.io()) //指生產事件在當前的執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) //指消費事件在主UI執行緒中進行
.subscribe(subscriber);
}
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
然後同樣的執行方式,在方法schedulerTest
上右擊滑鼠並run ‘schedulerTest()’執行測試!
然後你會看到彈窗
然後你會發現該指示是讓你啟動一個Android模擬器裝置,顯然是要執行到上面了。然而結果卻是app並不會到模擬器上啟動,而是”後臺執行”。以這種方式執行了我們的單元測試。
欣賞下Run
下的執行結果
然後欣賞下Android Monitor
下的執行結果
說明一切都在我們的掌握之中,能夠讓我看到該單元測試結果,並能夠確認該邏輯是否是我們想要的正確邏輯,從而起到單元測試的作用!
相關文章
- 單元測試:單元測試中的mockMock
- Go 單元測試之mock介面測試GoMock
- Java單元測試神器之MockitoJavaMockito
- Java單元測試技巧之PowerMockJavaMock
- 單元測試,只是測試嗎?
- Android 單元測試實踐Android
- 關於 Android 單元測試Android
- 測試開發之單元測試-禪道結合ZTF驅動單元測試執行
- 單元測試-【轉】論單元測試的重要性
- java中的單元測試Java
- 前端測試:Part II (單元測試)前端
- Android自動化測試入門(四)單元測試Android
- SpringBoot單元測試Spring Boot
- python 單元測試Python
- iOS 單元測試iOS
- Flutter 單元測試Flutter
- 單元測試 Convey
- 單元測試真
- golang單元測試Golang
- 單元測試工具
- 前端單元測試前端
- 十五、單元測試
- Go單元測試Go
- 聊聊單元測試
- Flutter 學習之路 - 測試(單元測試,Widget 測試,整合測試)Flutter
- 測試氣味-整潔單元測試
- 單元測試 - 測試場景記錄
- Java單元測試之JUnit 5快速上手Java
- Go 單元測試之Mysql資料庫整合測試GoMySql資料庫
- Go 單元測試之HTTP請求與API測試GoHTTPAPI
- 1.13-java單元測試junitJava
- 測試夜點心:單元測試測什麼
- JavaScript單元測試框架JavaScript框架
- 單元測試 -- mocha + chaiAI
- React元件單元測試React元件
- Spring Boot 單元測試Spring Boot
- Vue單元測試探索Vue
- Google 單元測試框架Go框架