其实,每次创建新的andorid工程的时候,都会自动导入JUnit,这是个啥?很多时候感觉这东西没用,在不经意间就删掉了,直到看到《Clean Code》,才发觉这个东西似乎很重要,于是开始自学。以下记录基本步骤:
1(model)build.gradle加入
(一般都会自动生成)
dependencies{
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
compile 'com.android.support.test.espresso:espresso-core:2.2.2'
compile 'com.android.support.test:runner:0.5'
compile 'com.android.support.test:rules:0.5'
如果不确定版本号,可以用File->Project Structure->点击需要进行单元测试的module->点击右侧面板上方的Dependencies->+号->Library Dependency->搜索开头部分例如com.android.support.test,找到对应项点击就会自动导入。
2.manifest中加入
都在application标签内(我加在外面就错了):
<uses-library android:name="android.test.runner"
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:label="@string/app_name"
android:targetPackage="."
3.普通测试:
在类名右键->Go To->Test->Create New Test,选择要测试的方法和setUp/@Before
这里参考了简书中的博客点击跳转
(普通代码部分添加:)
public class Calculator {
public double sum(double a, double b){
return a+b;
}
public int getAllBetweenNumsSum(int start,int end){
return (start+end)*(end-start+1)/2;
}
public double substract(double a, double b){
return 0;
}
public double divide(double a, double b){
return 0;
}
public double multiply(double a, double b){
return 0;
}
}
CaculatorTest中:
public class CalculatorTest
private Calculator mCalculator;
@Before
public void setUp() throws Exception {
mCalculator = new Calculator();
}
@Test
public void testSum() throws Exception {
testSum(2,3);
testSum(5,10);
testSum(-100,7);
}
private void testSum(int start,int end) {
int sum1 = mCalculator.getAllBetweenNumsSum(start,end);
int sum2 = getAllBetweenNumsSum2(start,end);
assertEquals(sum1,sum2);
}
public int getAllBetweenNumsSum2(int start,int end){
int sum = 0;
for(int i = start; i <= end; i++){
sum += i;
}
return sum;
}
@Test
public void testSubstract() throws Exception {
assertEquals(1d, mCalculator.substract(5d, 4d), 0);
}
@Test
public void testDivide() throws Exception {
assertEquals(4d, mCalculator.divide(20d, 5d), 0);
}
@Test
public void testMultiply() throws Exception {
assertEquals(10d, mCalculator.multiply(2d, 5d), 0);
}
}
可以看到,每个部分前面有个小箭头用于运行测试,当通过的时候,下面的测试条是绿色的,否则是红色的,通过测试条的颜色可以判断是否通过测试,assetEquals就是用来测试的,当内容不等时,就测试失败。这里的Caculate例子略有修改,我测了一下用(首数+尾数)*项数/2的公式是否写对。
android测试
实际上,你应该注意到,有两个测试包test和androidTest,test是对没有ui的部分进行计算测试,androidTest可以进行ui方面的测试,同样沿用简书的测试点击跳转:
编写一个测试的Activity:
public class TestUnitActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_unit);
}
public void sayHelloToEditInputContent(View v){
TextView txtShowBtnResult = (TextView) findViewById(R.id.txt_show_click_result);
EditText edtInputBtnContent = (EditText) findViewById(R.id.edt_input_click_content);
txtShowBtnResult.setText("Hello, " + edtInputBtnContent.getText().toString() + "!");
}
}
对应的xml布局文件:(原谅我有强迫症,一定要改EditView和TextView以及点击事件的名称)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
tools:context=".MainActivity">
<TextView
android:id="@+id/txt_show_click_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world!"
<EditText
android:id="@+id/edt_input_click_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/textView"
android:hint="Enter your name here"
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/edt_input_click_content"
android:onClick="sayHelloToEditInputContent"
android:text="Say hello!"
</RelativeLayout>
在androidTest部分新建一个类:
public class MainActivityInstrumentationTest
private static final String STRING_TO_BE_TYPED = "Peter";
@Rule
public ActivityTestRule<TestUnitActivity> mActivityRule = new ActivityTestRule<>(
TestUnitActivity.class);
@Test
public void sayHello(){
onView(withId(R.id.edt_input_click_content)).perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); //line 1
onView(withText("Say hello!")).perform(click()); //line 2
String expectedText = "Hello, " + STRING_TO_BE_TYPED + "!";
onView(withId(R.id.txt_show_click_result)).check(matches(withText(expectedText))); //line 3
line 1:对具有指定id的控件进行输入指定字符串,然后关闭键盘。
line 2:点击显示Say hello内容的控件。
line 3:比较具有指定id的控件文本是否和指定文本一致。
异步例子,来自这里:
private String temp = null;
public void testAsyncTask() throws Throwable {
final CountDownLatch signal = new CountDownLatch(1);
// 在UI线程中执行异步操作
runTestOnUiThread(new Runnable() {
@Override
public void run() {
// 执行异步任务,这个只是例子,没有实际的可以运行的代码,大家知道就好
new MyAsyncTask() {
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
temp = result;
signal.countDown();
}
}.execute();
}
});
signal.await();
assertEquals("kale", temp);
}
个人感受:对于非ui部分测试较简单,写AssertEquals就能完成大多数的测试;对于ui部分如果测试比较麻烦,尤其是一些自定义事件之类,有回调的代码,甚至onTouchListener这种很难描述的东西【什么时候按下,按下哪里,拖动速度,拖动到哪一点?过程中显示是否闪烁?这决定了会有无数的例子,单元测试应该搞不定】。以后有时间会买本书专门学习,非ui的逻辑部分会用单元测试,ui部分的逻辑可以独立出来暂时public给单元测试用用看。
总结:
单元测试对于逻辑测试好用,ui看情况用,个人会用于逻辑测试。另外,一定要控制好方法参数数量,不然有五个参数,就算每个都是boolean,就有32种情况,呵呵,测死你。建议控制在3个以内。