[Module] 05 - Junit, Crash reporting and Hot Fix
JUNIT测试
概览
在Android Studio中使用JUnit4进行单元测试(一)【有代码配合】
JUnit4注解解释
1. @Test : 测试方法,测试程序会运行的方法,后边可以跟参数代表不同的测试,如(expected=XXException.class) 异常测试,(timeout=xxx)超时测试
2. @Ignore : 被忽略的测试方法
3. @Before: 每一个测试方法之前运行
4. @After : 每一个测试方法之后运行
5. @BeforeClass: 所有测试开始之前运行
6. @AfterClass: 所有测试结束之后运行
添加依赖:

在CalculatorTest中写相对应的测试方法即可:

鼠标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的一个组件在运行在系统指定的生命周期中。举个例子:
- 一个Activity对象的生命周期开始就是被Intent激活的时候,系统调用该对象的onCreate()方法,然后调用onResume()方法,
- 当用户在切换到别的应用的时候,系统又调用onPause()方法,
- 如果在Activity的代码中调用finish()方法时,系统则会调用的onDestroy()方法。
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内容是否包含"账户名" } }
关于测试,是个体系,也是个不小的篇章,将另起随笔学习。
以下是不错的学习材料:
- Android单元测试:Mockito使用详解
- Android单元测试:使用本地数据测试Retrofit
- Android单元测试:测试RxJava的同步及异步操作
- Android 自动化测试 Espresso篇:简介&基础使用
- Android 自动化测试 Espresso篇:异步代码测试
关于DataBinding,请见博客:Android MVVM+DataBinding结合Dagger2进行开发【该博客的系列文章不错】
Crash reporting
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
Hot Fix
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律