在Android Studio中使用JUnit4进行单元测试(一)【有代码配合】html
JUnit4注解解释 java
1. @Test : 测试方法,测试程序会运行的方法,后边能够跟参数表明不一样的测试,如(expected=XXException.class) 异常测试,(timeout=xxx)超时测试android
2. @Ignore : 被忽略的测试方法ios
3. @Before: 每个测试方法以前运行编程
4. @After : 每个测试方法以后运行数组
5. @BeforeClass: 全部测试开始以前运行网络
6. @AfterClass: 全部测试结束以后运行架构
添加依赖:app
鼠标focus要测试的函数,鼠标右键菜单,运行该测试函数便可。框架
package com.lp.knightoneadmin.android_junit_tutorial.junit;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@Test public void testAdd(){ int sum = mCalculator.add(3, 4); Assert.assertEquals(7,sum); }
Goto: https://www.jianshu.com/p/03118c11c199 在Android Studio中进行单元测试和UI测试 - 1.概述 在Android Studio中进行单元测试和UI测试 - 2.建立新的Android Studio工程 在Android Studio中进行单元测试和UI测试 - 3.配置支持单元测试的工程 在Android Studio中进行单元测试和UI测试 - 4.建立第一个单元测试 在Android Studio中进行单元测试和UI测试 - 5.运行单元测试 在Android Studio中进行单元测试和UI测试 - 6.配置支持Instrumentation测试的工程 在Android Studio中进行单元测试和UI测试 - 7.为app添加简单的交互 在Android Studio中进行单元测试和UI测试 - 8.建立并运行Espresso测试 在Android Studio中进行单元测试和UI测试 - 9.祝贺!
你的测试代码和你创建并运行Android Studio中的测试方式的结构 取决于 测试你正在执行的 类型。
下表总结了常见Android的测试类型:
Type | Subtype | Description |
Unit tests | Local Unit Tests | Unit tests that run on your local machine only. These tests are compiled to run locally on the JVM to minimize execution time. Use this approach to run unit tests that have no dependencies on the Android framework or have dependencies that mock objects can satisfy. |
Instrumented unit tests | Unit tests that run on an Android device or emulator. These tests have access toInstrumentation information, such as the Context of the app under test. Use this approach to run unit tests that have Android dependencies which mock objects cannot easily satisfy. | |
Integration Tests | Components within your app only | This type of test verifies that the target app behaves as expected when a user performs a specific action or enters a specific input in its activities. For example, it allows you to check that the target app returns the correct UI output in response to user interactions in the app’s activities. UI testing frameworks like Espresso allow you to programmatically simulate user actions and test complex intra-app user interactions. |
Cross-app Components | This type of test verifies the correct behavior of interactions between different user apps or between user apps and system apps. For example, you might want to test that your app behaves correctly when the user performs an action in the Android Settings menu. UI testing frameworks that support cross-app interactions, such as UI Automator, allow you to create tests for such scenarios. |
Instrumentation
Android Instrumentation在安卓系统上是一组控制函数或者是hooks(钩子)。这些钩子在本身的生命周期独立的控制一个安卓组件,他们也控制着安卓如何加载应用程序。
下图总结了Instrumentation的测试框架:
一般状况下,Android的一个组件在运行在系统指定的生命周期中。举个例子:
Android框架的API不提供对你的代码直接调用这些回调函数,但你能够经过Instrumentation来完成。
系统运行一个应用的全部组件都是在同一个进程中,你可让某些组件(例如content providers)在单独的进程中运行,可是你不能强制让一个应用程序和另外一个已经运行的程序运行在同一个进程中。
Instrumentation能够同时加载。一旦你的应用程序和你的测试程序在一个进程当中了,你的测试程序就能够调用组件中的方法,而且在组件中修改和验证变量。
看过上述内容,有点蒙!
仍是系统的学下为好!
Ref: Android Testing Support【教学视频】
测试体系金字塔:
4. Other |
3. End-to-end tests |
2. Integration, UI test |
1. Unit tests |
Ref: Android 自动化测试 Espresso篇:简介&基础使用【看上去比较靠谱】
提问:UI测试该怎么搞?
其实相比较于单元测试,Espresso我我的理解更倾向于像自动化集成测试,
由于这个库实质上是开发者进行测试的时候,在测试设备上面安装了两个apk(开发者通常的apk和AndroidTest apk),
而后模拟操做(好比点击按钮,滑动列表等等)在界面上,将开发者预期的结果和实际的结果进行对比。
劣势:Espresso测试 对比 单元测试(UnitTest),前者耦合度太大。
优点:咱们可让Espresso处理它拿手的UI界面测试;网络请求等业务处理,咱们能够交给其余测试框架去处理,好比Mockito(后文再讲)。
适合MVP架构:MVP模式将数据的展现处理交给了View,业务代码交给了Model,咱们彻底能够经过MVP模式,将测试代码分开来测试。
写一段代码:
@OnClick({R.id.btn01, R.id.btn02}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.btn01: tvContent.setVisibility(View.VISIBLE); tvContent.setText("hello espresso!"); break; case R.id.btn02: tvContent.setVisibility(View.VISIBLE); tvContent.setText("success"); et01.setText(""); break; } }
为了测试这一段代码,须要写多少对应的测试代码?
@RunWith(AndroidJUnit4.class) public class A01SimpleActivityTest { @Rule public ActivityTestRule<A01SimpleActivity> rule = new ActivityTestRule<>(A01SimpleActivity.class); @Test public void clickTest() { //tvContent是否默认不显示 onView(ViewMatchers.withId(R.id.tv_content)) .check(matches(not(isDisplayed()))); //是否不可见 //检查btn01的text,而后执行点击事件 onView(withId(R.id.btn01)) .check(matches(withText("修改内容"))) .perform(click()); //检查tv内容是否修改,而且是否可见 onView(withId(R.id.tv_content)) .check(matches(withText("hello espresso!"))) .check(matches(isDisplayed())); } @Test public void loginTest() throws Exception { //先清除editText的内容,而后输入,而后关闭软键盘,最后校验内容 //这里若是要输入中文,使用replaceText()方法代替typeText() onView(withId(R.id.et_01)) .perform(clearText(), replaceText("你好 username"), closeSoftKeyboard()) .check(matches(withText("你好 username"))); //点击登陆 onView(withId(R.id.btn02)) .perform(click()); //校验内容 onView(withId(R.id.tv_content)) .check(matches(withText("success"))) .check(matches(isDisplayed())); onView(withId(R.id.et_01)) .check(matches(withText(""))) //内容是否为"" .check(matches(withHint("请输入帐户名"))) //hint内容是否为"请输入帐户名" .check(matches(withHint(containsString("帐户名")))); //hint内容是否包含"帐户名" } }
关于测试,是个体系,也是个不小的篇章,将另起随笔学习。
如下是不错的学习材料:
关于DataBinding,请见博客:Android MVVM+DataBinding结合Dagger2进行开发【该博客的系列文章不错】
Ref: Crash Reporting - Android Firebase
1. 添加依赖:firebase-crash
我的理解的关键,仍是合理使用try...catch编程习惯的问题。
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private Button btnError1, btnError2, btnError3; private EditText mText1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
btnError1 = (Button) findViewById(R.id.btnError1); btnError2 = (Button) findViewById(R.id.btnError2); btnError3 = (Button) findViewById(R.id.btnError3); mText1 = (EditText) findViewById(R.id.editText); Log.d(TAG, "onCreate: starting."); FirebaseCrash.log("onCreate started."); btnError1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FirebaseCrash.log("btnError1 Clicked."); //this will throw an exception and report it String text = null; mText1.setText(text.toString()); // null的toString抛出异常 --> fatal error, 直接致使"程序crash" } }); btnError2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FirebaseCrash.log("btnError2 Clicked."); String filepath = "sdcard/made-up/filepath/"; try{ File file = new File(filepath); // path是假的,但本地程序已有处理,属于non-fatal error,firebase也会得到记录 InputStream inputStream = new FileInputStream(file); inputStream.read(); }catch (FileNotFoundException e){ FirebaseCrash.report(new Exception( "FileNotFoundException in btnError2. Probably the filepath:" + filepath )); } catch (IOException e) { FirebaseCrash.report(new Exception( e.toString() )); } } }); btnError3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { btnError3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FirebaseCrash.log("btnError3 Clicked."); ArrayList<String> theList = new ArrayList<>(); theList.add("String 1"); theList.add("String 2"); theList.add("String 3"); //this will throw an exception because the index is out of bounds for (int i = 0; i <= theList.size(); i++){ Log.d(TAG, "onClick: theList: " + theList.get(i)); // 数组index溢出 --> fatal error } } }); } }); } }
firebase得到error报告:
常见异常种类:Goto 菜鸟教程 - Java 异常处理 or [Android] 01 - Java Basic for Android