关于Android的单元测试,不仅仅可以确保某个模块的正确性,在比较大的项目当中,也可以保证整个项目的合理运行。其单元测试小到可以应用于一个Application,大到一个Activity中的内容。单元测试是否通过,在于给出一系列的输入,看输出的结果是否是我们所期望的。
网上有许多关于单元测试的博客,大多都以demo的形式来呈现的,我们也不例外,这里只是简单介绍下有关于单元测试的一些基本内容,其实还有许多可以深入的地方,但我们这个例子只是对一些浅显内容的说明,辅助以一些注释。
首先最主要的是对配置文件的一些说明,在android工程中的AndroidMainFest.xml文件里,在<application 标签中加入<uses-library android:name="android.test.runner"这一句,相当于注册了相关的测试环境
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cheng.myapplication" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ResultActivity">
</activity>
<uses-library android:name="android.test.runner" />
</application>
</manifest>
我们先从一个小的Application开始,就简单弄个加法运算的函数吧,放在一个类之中
public class UnitTestApplication extends Application{
@Override
public void onCreate(){
super.onCreate();
}
@Override
public void onTerminate(){
super.onTerminate();
}
public int add(int firstNumber, int secondNumber){
return firstNumber + secondNumber;
}
}
public class UnitTestApplicationTest extends ApplicationTestCase<UnitTestApplication> {
private UnitTestApplication unitTestApplication;
public UnitTestApplicationTest() {
//在构造函数中调用父类构造函数,参数为被测试的类
super(UnitTestApplication.class);
}
@Override
protected void setUp() throws Exception{
super.setUp();
createApplication();
//获取待测试的UnitTestApplication
unitTestApplication = getApplication();
}
//测试UnitTestApplication类中的add函数(注意测试函数的命名必须以test开头)
public void testadd(){
assertEquals(3, unitTestApplication.add(1, 2));
}
}
这个类需要注意的几点:
1、测试类的构造函数调用父类的构造函数,其参数为被测试的类
2、在setUp函数中获取待测试的类
3、测试函数的命名必须以test开头
以上就是一个简单的application的测试类,下面我们来看看Activity的
我们这个Activity的功能十分简单,就一个输入用户名,密码的登录操作,界面截图过几天贴上
先来看看我们的xml布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:orientation="vertical">
<EditText
android:id="@+id/user_name_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入用户名"/>
<EditText
android:id="@+id/pass_word_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入密码"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/login_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="登录"/>
<Button
android:id="@+id/clear_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="清空"/>
</LinearLayout>
</LinearLayout>
public class MainActivity extends ActionBarActivity implements View.OnClickListener{
private EditText mUserNameEt;
private EditText mPassWordEt;
private Button mLoginBtn;
private Button mClearBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
setListener();
}
private void findView(){
mUserNameEt = (EditText)findViewById(R.id.user_name_et);
mPassWordEt = (EditText)findViewById(R.id.pass_word_et);
mLoginBtn = (Button)findViewById(R.id.login_btn);
mClearBtn = (Button)findViewById(R.id.clear_btn);
}
private void setListener(){
mLoginBtn.setOnClickListener(this);
mClearBtn.setOnClickListener(this);
}
@Override
public void onClick(View view){
int id = view.getId();
switch (id){
case R.id.login_btn:
Intent intent = new Intent(this, ResultActivity.class);
intent.putExtra("userName", mUserNameEt.getText().toString());
intent.putExtra("passWord", mPassWordEt.getText().toString());
startActivity(intent);
break;
case R.id.clear_btn:
mUserNameEt.setText("");
mPassWordEt.setText("");
break;
default:
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
输入用户名和密码,点击登录,就会跳到下个页面ResultActivity.java,显示输入过的用户名和密码,点击清空,就会清空输入的用户名和密码
再来看看ResultActivity.java类和其xml文件吧
public class ResultActivity extends Activity{
private TextView mResultTv;
private String mUserName;
private String mPassWord;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.result_activity);
findView();
//获取从MainActivity传来的intent信息
Intent intent = getIntent();
mUserName = intent.getStringExtra("userName");
mPassWord = intent.getStringExtra("passWord");
mResultTv.setText(mUserName+ " "+mPassWord);
}
private void findView(){
mResultTv = (TextView)findViewById(R.id.result_tv);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/result_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
好了,接下来可以实现我们对这个MainActivity的测试类了
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity>{
private Instrumentation mInstrumentation;
private MainActivity mMainActivity;
private EditText mUserNameEt;
private EditText mPassWordEt;
private Button mLoginBtn;
private Button mClearBtn;
public MainActivityTest(){
super(MainActivity.class);
}
@Override
protected void setUp() throws Exception{
super.setUp();
setActivityInitialTouchMode(false);
mInstrumentation = getInstrumentation();
mMainActivity = getActivity();
mUserNameEt = (EditText)mMainActivity.findViewById(R.id.user_name_et);
mPassWordEt = (EditText)mMainActivity.findViewById(R.id.pass_word_et);
mLoginBtn = (Button)mMainActivity.findViewById(R.id.login_btn);
mClearBtn = (Button)mMainActivity.findViewById(R.id.clear_btn);
}
//输入相关信息
public void input(){
//用户名操作
mMainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mUserNameEt.requestFocus();
mUserNameEt.performClick();
}
});
mInstrumentation.waitForIdleSync();
//输入用户名
sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_3);
//密码操作
mMainActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mPassWordEt.requestFocus();
mPassWordEt.performClick();
}
});
//输入密码
sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_3);
}
//测试输入信息
public void testInput(){
input();
assertEquals("123", mUserNameEt.getText().toString());
assertEquals("123", mPassWordEt.getText().toString());
}
//测试登录按钮
public void testLogin(){
input();
mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
mLoginBtn.requestFocus();
mLoginBtn.performClick();
}
});
}
//测试清空按钮
public void testClear(){
input();
mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
mClearBtn.requestFocus();
mClearBtn.performClick();
}
});
assertEquals("", mUserNameEt.getText().toString());
assertEquals("", mPassWordEt.getText().toString());
}
}
其测试流程都按照我们实际的操作流程来进行,首先肯定是输入用户名和密码的操作,在input函数中,我调用了sendKeys函数,让系统测试的时候自动输入函数中的参数内容。
一共有三个测试函数,分别测试了输入的信息testInput,登录按钮testLogin,清空按钮testClear。
如果全部测试通过的话,那么系统的进度条会显示的是绿色样式,如果有其中一个不过,就说明输出的结果并不是正确的期望结果,进度条为红色样式。
此篇文章只浅显的讲了单元测试的一些基本流程,以后本人还会更加深入探索。