测试 之Java单元测试、Android单元测试

测试 之Java单元测试、Android单元测试
文章图片

我的目的,旨在介绍一个不一样的“单元测试” !
其实对于单元测试这一块,我很早已经开始关注了,也搜罗了好多这方面技术的博客。要么只有文字性的,要么都是代码的逻辑类,笼统、没有什么脉络可循。之后依然半解,放到项目中,用起来还是不方便,就是觉得这样比我直接运行在模拟器(真机)之后的过程打印、调试要慢好多!
因此,就导致后来的放弃。以及今天的再次拾起,并做一个系统点的介绍,希望特别想要使用单元测试的朋友能够用得着。
#不一样 、不一样的单元测试
测试 之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"

【测试 之Java单元测试、Android单元测试】####代码类配置的区别
她们之间有明眼的区别,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单元测试、Android单元测试
文章图片

点击确定进入新的页面框,走入目录
测试 之Java单元测试、Android单元测试
文章图片

当然是选择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()'
测试 之Java单元测试、Android单元测试
文章图片

打印结果:
测试 之Java单元测试、Android单元测试
文章图片

如果我换一个单元测试类,这时用到了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 subscriber = new Subscriber() { @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() {@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 + '\'' + '}'; } }}

接下来你会看到报错、报错
测试 之Java单元测试、Android单元测试
文章图片

但如果,我把第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 subscriber = new Subscriber() { @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() {@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()’**执行测试!
测试 之Java单元测试、Android单元测试
文章图片

然后你会看到弹窗
测试 之Java单元测试、Android单元测试
文章图片

然后你会发现该指示是让你启动一个Android模拟器设备,显然是要运行到上面了。然而结果却是app并不会到模拟器上启动,而是"后台运行"。以这种方式执行了我们的单元测试。
欣赏下Run下的执行结果
测试 之Java单元测试、Android单元测试
文章图片

然后欣赏下Android Monitor下的执行结果
测试 之Java单元测试、Android单元测试
文章图片

说明一切都在我们的掌握之中,能够让我看到该单元测试结果,并能够确认该逻辑是否是我们想要的正确逻辑,从而起到单元测试的作用!

    推荐阅读