要了解android单元测试,首先必须了解junit
什么是 JUnit ?
JUnit是采用测试驱动开发的方式,也就是说在开发前先写好测试代码,主要用来说明被测试的代码会被如何使用,错误处理等;然后开始写代码,并在测试代码中逐步测试这些代码,直到最后在测试代码中完全通过。
现简要说JUnit的4大功能
- 管理测试用例。修改了哪些代码,这些代码的修改会对哪些部分有影响,通过JUnit将这次的修改做个完整测试。这也就JUnit中所谓的TestSuite。
- 定义测试代码。这也就是JUnit中所谓的TestCase,根据源代码的测试需要定义每个TestCase,并将TestCase添加到相应的TestSuite方便管理。
- 定义测试环境。在TestCase测试前会先调用“环境”配置,在测试中使用,当然也可以在测试用例中直接定义测试环境。
- 检测测试结果。对于每种正常、异常情况下的测试,运行结果是什么、结果是否是我们预期的等都需要有个明确的定义,JUnit在这方面提供了强大的功能。
以上部分与我们平常使用IDE调试的过程是完全一样的,只不过是增加了测试用例管理、测试结果检测等功能,提高了单元的效率,保证了单元测试的完整性,明确了单元测试的目标。
一个 JUnit 测试包含以下元素:
开发代码部分 | 测试代码部分 | 测试工具部分 |
待测试类 A | 通过扩展 TestCase 或者构造 TestSuit 方法 编写测试类 B | 一个测试运行器(TestRunner)R,可以选择图形界面或文本界面 |
操作步骤:
将 B 通过命令行方式或图形界面选择方式传递给 R,R 自动运行测试,并显示结果。
首先看下junit测试类库和android中单元测试类库:
SDK | 功能说明 |
junit.framework | JUnit测试框架 |
junit.runner | 实用工具类支持JUnit测试框架 |
android.test | Android 对JUnit测试框架的扩展包 |
android.test.mock | Android的一些辅助类 |
android.test.suitebuilder | 实用工具类,支持类的测试运行 |
junit.framework:
- TestSuit:
TestSuite
是测试用例的集合; - TestCase:定义运行多个测试用例;
- TestResult:收集一个测试案例的结果,测试结果分为失败和错误,如果未能预计的断言就是失败,错误就像一个ArrayIndexOutOfBoundsException异常而导致的无法预料的问题;
- TestFailure:测试失败时捕获的异常;
- Assert:断言的方法集,当断言失败时显示信息;
android.test包:
JUnit TestCase类
继承自JUnit的TestCase,不能使用Instrumentation框架。但这些类包含访问系统对象(如Context)的方法。使用Context,你可以浏览资源,文件,数据库等等。基类是AndroidTestCase,一般常见的是它的子类,和特定组件关联。
子类有:
l ApplicationTestCase——测试整个应用程序的类。它允许你注入一个模拟的Context到应用程序中,在应用程序启动之前初始化测试参数,并在应用程序结束之后销毁之前检查应用程序。
l ProviderTestCase2——测试单个ContentProvider的类。因为它要求使用MockContentResolver,并注入一个IsolatedContext,因此Provider的测试是与OS孤立的。
l ServiceTestCase——测试单个Service的类。你可以注入一个模拟的Context或模拟的Application(或者两者),或者让Android为你提供Context和MockApplication。
Instrumentation TestCase类
继承自JUnit TestCase类,并可以使用Instrumentation框架,用于测试Activity。使用Instrumentation,Android可以向程序发送事件来自动进行UI测试,并可以精确控制Activity的启动,监测Activity生命周期的状态。
基类是InstrumentationTestCase。它的所有子类都能发送按键或触摸事件给UI。子类还可以注入一个模拟的Intent。
子类有:
l ActivityTestCase——Activity测试类的基类。
l SingleLaunchActivityTestCase——测试单个Activity的类。它能触发一次setup()和tearDown(),而不是每个方法调用时都触发。如果你的测试方法都是针对同一个Activity的话,那就使用它吧。
l SyncBaseInstrumentation——测试Content Provider同步性的类。它使用Instrumentation在启动测试同步性之前取消已经存在的同步对象。
l ActivityUnitTestCase——对单个Activity进行单一测试的类。使用它,你可以注入模拟的Context或Application,或者两者。它用于对Activity进行单元测试。
不同于其它的Instrumentation类,这个测试类不能注入模拟的Intent。
l ActivityInstrumentationTestCase2——在正常的系统环境中测试单个Activity的类。你不能注入一个模拟的Context,但你可以注入一个模拟的Intent。另外,你还可以在UI线程(应用程序的主线程)运行测试方法,并且可以给应用程序UI发送按键及触摸事件。
下面找了几个例子:
首先看junit,然后在看android test:
junit:
待测试类A:
1
2
3
4
5
6
7
|
package
cn.edu.wtu.junit;
public
class
Calcuator {
public
double
add(
double
n1,
double
n2) {
return
n1 + n1;
}
}
|
测试代码B:扩展testcase
1
2
3
4
5
6
7
8
9
10
11
12
|
package
cn.edu.wtu.junit;
import
junit.framework.TestCase;
public
class
TestCalcuator
extends
TestCase {
public
void
testAdd(){
Calcuator calcuator=
new
Calcuator();
double
result=calcuator.add(
1
,
2
);
assertEquals(
3
,result,
0
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
testsuit 测试容器:
package
cn.edu.wtu.junit;
import
junit.framework.Test;
import
junit.framework.TestSuite;
import
junit.textui.TestRunner;
public
class
TestAll
extends
TestSuite {
public
static
Test suite() {
TestSuite suite =
new
TestSuite(
"TestSuite Test"
);
suite.addTestSuite(TestCalcuator.
class
);
suite.addTestSuite(TestCalcuator2.
class
);
return
suite;
}
public
static
void
main(String args[]){
// 命令行输出 测试工具 一个测试运行器
TestRunner.run(suite());
}
}
|
run on junit 图形界面显示:
run java application:控制台输出:
android test :
首先看下非instrumentation框架测试:
测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package
aexp.junit;
import
android.test.AndroidTestCase;
import
android.util.Log;
public
class
MathTest
extends
AndroidTestCase
{
protected
int
i1;
protected
int
i2;
static
final
String LOG_TAG =
"MathTest"
;
public
void
setUp()
{
i1 =
2
;
i2 =
3
;
}
public
void
testAdd()
{
Log.d( LOG_TAG,
"testAdd"
);
assertTrue( LOG_TAG+
"1"
, ( ( i1 + i2 ) ==
5
) );
}
public
void
testAndroidTestCaseSetupProperly()
{
super
.testAndroidTestCaseSetupProperly();
Log.d( LOG_TAG,
"testAndroidTestCaseSetupProperly"
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
package
aexp.junit;
import
android.content.ContentResolver;
import
android.content.ContentValues;
import
android.database.Cursor;
import
android.net.Uri;
import
android.provider.Contacts;
import
android.util.Log;
import
android.test.AndroidTestCase;
public
class
ContactTest
extends
AndroidTestCase
{
static
final
String LOG_TAG =
"ContactTest"
;
static
final
String TESTUSER_NAME =
"Test User"
;
static
final
String TESTUSER_NOTES =
"Test note"
;
ContentResolver contentResolver;
Uri newPerson;
public
void
setUp()
{
contentResolver = getContext().getContentResolver();
ContentValues person =
new
ContentValues();
person.put(Contacts.People.NAME, TESTUSER_NAME );
person.put(Contacts.People.NOTES, TESTUSER_NOTES );
newPerson = contentResolver.insert(
Contacts.People.CONTENT_URI,person);
}
public
void
testInsertContact()
{
Log.d( LOG_TAG,
"testInsertContact"
);
assertNotNull( newPerson );
}
public
void
testQueryContact()
{
Log.d( LOG_TAG,
"testQueryContact"
);
String columns[] = { Contacts.People.NAME,
Contacts.People.NOTES };
Cursor c = contentResolver.query( Contacts.People.CONTENT_URI,
columns,
Contacts.People.NAME+
"=?"
,
new
String[] { TESTUSER_NAME },
null
);
assertNotNull( c );
int
hits =
0
;
while
( c.moveToNext() )
{
int
nameColumnIndex = c.getColumnIndex( Contacts.People.NAME );
int
notesColumnIndex = c.getColumnIndex( Contacts.People.NOTES );
String name = c.getString( nameColumnIndex );
String notes = c.getString( notesColumnIndex );
Log.d( LOG_TAG,
"retrieved name: "
+name );
Log.d( LOG_TAG,
"retrieved notes: "
+notes );
assertEquals( TESTUSER_NAME, name );
assertEquals( TESTUSER_NOTES, notes );
++hits;
}
assertEquals( hits,
1
);
c.close();
}
public
void
tearDown()
{
contentResolver.delete( newPerson,
null
,
null
);
}
}
|
子树:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package
aexp.junit;
import
junit.framework.TestSuite;
import
junit.framework.Assert;
public
class
SomeTest
extends
TestSuite {
public
void
testSomething()
throws
Throwable
{
Assert.assertTrue(
1
+
1
==
2
);
}
public
void
testSomethingElse()
throws
Throwable
{
Assert.assertTrue(
1
+
1
==
3
);
}
}
|
测试树:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package
aexp.junit;
import
junit.framework.TestSuite;
public
class
ExampleSuite
extends
TestSuite
{
public
ExampleSuite()
{
addTestSuite( MathTest.
class
);
addTestSuite( ContactTest.
class
);
addTestSuite(SomeTest.
class
);
}
}
|
测试运行器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
package
aexp.junit;
import
android.app.Activity;
import
android.os.Bundle;
import
android.test.AndroidTestCase;
import
android.test.AndroidTestRunner;
import
android.widget.Button;
import
android.widget.TextView;
import
android.widget.Toast;
import
android.view.View;
import
android.util.Log;
import
junit.framework.TestListener;
import
junit.framework.Test;
import
junit.framework.AssertionFailedError;
public
class
JUnit
extends
Activity {
static
final
String LOG_TAG =
"junit"
;
Thread testRunnerThread =
null
;
/** Called when the activity is first created. */
@Override
public
void
onCreate(Bundle savedInstanceState)
{
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button launcherButton = (Button)findViewById( R.id.launch_button );
launcherButton.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick( View view ) {
startTest();
}
} );
}
private
synchronized
void
startTest()
{
if
( ( testRunnerThread !=
null
) &&
!testRunnerThread.isAlive() )
testRunnerThread =
null
;
if
( testRunnerThread ==
null
) {
testRunnerThread =
new
Thread(
new
TestRunner(
this
) );
testRunnerThread.start();
}
else
Toast.makeText(
this
,
"Test is still running"
,
Toast.LENGTH_SHORT).show();
}
}
//显示线程
class
TestDisplay
implements
Runnable
{
public
enum
displayEvent{START_TEST,END_TEST,ERROR,FAILURE,}
displayEvent ev;
String testName;
int
testCounter;
int
errorCounter;
int
failureCounter;
TextView statusText;
TextView testCounterText;
TextView errorCounterText;
TextView failureCounterText;
public
TestDisplay( displayEvent ev,
String testName,
int
testCounter,
int
errorCounter,
int
failureCounter,
TextView statusText,
TextView testCounterText,
TextView errorCounterText,
TextView failureCounterText )
{
this
.ev = ev;
this
.testName = testName;
this
.testCounter = testCounter;
this
.errorCounter = errorCounter;
this
.failureCounter = failureCounter;
this
.statusText = statusText;
this
.testCounterText = testCounterText;
this
.errorCounterText = errorCounterText;
this
.failureCounterText = failureCounterText;
}
public
void
run()
{
StringBuffer status =
new
StringBuffer();
switch
( ev ) {
case
START_TEST:
status.append(
"Starting"
);
break
;
case
END_TEST:
status.append(
"Ending"
);
break
;
case
ERROR:
status.append(
"Error: "
);
break
;
case
FAILURE:
status.append(
"Failure: "
);
break
;
}
status.append(
": "
);
status.append( testName );
statusText.setText(
new
String( status ) );
testCounterText.setText(
"Tests: "
+testCounter );
errorCounterText.setText(
"Errors: "
+errorCounter );
failureCounterText.setText(
"Failure: "
+failureCounter );
}
}
class
TestRunner
implements
Runnable,TestListener
{
static
final
String LOG_TAG =
"TestRunner"
;
int
testCounter;
int
errorCounter;
int
failureCounter;
TextView statusText;
TextView testCounterText;
TextView errorCounterText;
TextView failureCounterText;
Activity parentActivity;
public
TestRunner( Activity parentActivity )
{
this
.parentActivity = parentActivity;
}
public
void
run()
{
testCounter =
0
;
errorCounter =
0
;
failureCounter =
0
;
statusText = (TextView)parentActivity.
findViewById( R.id.status );
testCounterText = (TextView)parentActivity.
findViewById( R.id.testCounter );
errorCounterText = (TextView)parentActivity.
findViewById( R.id.errorCounter );
failureCounterText = (TextView)parentActivity.
findViewById( R.id.failureCounter );
Log.d( LOG_TAG,
"Test started"
);
// 测试运行器
AndroidTestRunner testRunner =
new
AndroidTestRunner();
testRunner.setTest(
new
ExampleSuite() );
testRunner.addTestListener(
this
);
testRunner.setContext( parentActivity );
testRunner.runTest();
Log.d( LOG_TAG,
"Test ended"
);
}
// TestListener
public
void
addError(Test test, Throwable t)
{
Log.d( LOG_TAG,
"addError: "
+test.getClass().getName() );
Log.d( LOG_TAG, t.getMessage(), t );
++errorCounter;
TestDisplay td =
new
TestDisplay(
TestDisplay.displayEvent.ERROR,
test.getClass().getName(),
testCounter,
errorCounter,
failureCounter,
statusText,
testCounterText,
errorCounterText,
failureCounterText );
parentActivity.runOnUiThread( td );
}
public
void
addFailure(Test test, AssertionFailedError t)
{
Log.d( LOG_TAG,
"addFailure: "
+test.getClass().getName() );
Log.d( LOG_TAG, t.getMessage(), t );
++failureCounter;
TestDisplay td =
new
TestDisplay(
TestDisplay.displayEvent.FAILURE,
test.getClass().getName(),
testCounter,
errorCounter,
failureCounter,
statusText,
testCounterText,
errorCounterText,
failureCounterText );
parentActivity.runOnUiThread( td );
}
public
void
endTest(Test test)
{
Log.d( LOG_TAG,
"endTest: "
+test.getClass().getName() );
TestDisplay td =
new
TestDisplay(
TestDisplay.displayEvent.END_TEST,
test.getClass().getName(),
testCounter,
errorCounter,
failureCounter,
statusText,
testCounterText,
errorCounterText,
failureCounterText );
parentActivity.runOnUiThread( td );
}
public
void
startTest(Test test)
{
Log.d( LOG_TAG,
"startTest: "
+test.getClass().getName() );
++testCounter;
TestDisplay td =
new
TestDisplay(
TestDisplay.displayEvent.START_TEST,
test.getClass().getName(),
testCounter,
errorCounter,
failureCounter,
statusText,
testCounterText,
errorCounterText,
failureCounterText );
parentActivity.runOnUiThread( td );
}
}
|
instrumentation框架:
首先新建一个android工程:
里面编写3个activity:MainActivity,HomeActivity,LoginActivity
MainActivity是加载界面,LoginActivity是登陆界面,HomeActivity是最终界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package
cn.edu.wtu.junit;
import
android.app.Activity;
import
android.content.Intent;
import
android.os.Bundle;
import
android.util.Log;
import
android.view.View;
public
class
MainActivity
extends
Activity {
private
static
final
boolean
DEBUG =
true
;
private
static
final
String TAG =
"-- MainActivity"
;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
if
(DEBUG) {
Log.i(TAG,
"onCreate"
);
}
super
.onCreate(savedInstanceState);
setContentView(R.layout.act_main);
View toLoginView = findViewById(R.id.to_login);
toLoginView.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick(View view) {
if
(DEBUG) {
Log.i(TAG,
"toLoginView clicked"
);
}
Intent intent =
new
Intent(getApplicationContext(), LoginActivity.
class
);
startActivity(intent);
}
});
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package
cn.edu.wtu.junit;
import
android.app.Activity;
import
android.content.Intent;
import
android.os.Bundle;
import
android.util.Log;
import
android.view.View;
import
android.widget.EditText;
public
class
LoginActivity
extends
Activity {
private
static
final
boolean
DEBUG =
true
;
private
static
final
String TAG =
"-- LoginActivity"
;
private
EditText mUsernameView;
private
EditText mPasswordView;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
if
(DEBUG) {
Log.i(TAG,
"onCreate"
);
}
super
.onCreate(savedInstanceState);
setContentView(R.layout.act_login);
mUsernameView = (EditText) findViewById(R.id.username);
mPasswordView = (EditText) findViewById(R.id.password);
View submitView = findViewById(R.id.submit);
submitView.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick(View view) {
if
(DEBUG) {
Log.i(TAG,
"submitView clicked"
);
}
Intent intent =
new
Intent(getApplicationContext(), HomeActivity.
class
);
intent.putExtra(HomeActivity.EXTRA_USERNAME, mUsernameView.getText().toString());
intent.putExtra(HomeActivity.EXTRA_PASSWORD, mPasswordView.getText().toString());
startActivity(intent);
}
});
View resetView = findViewById(R.id.reset);
resetView.setOnClickListener(
new
View.OnClickListener() {
public
void
onClick(View view) {
if
(DEBUG) {
Log.i(TAG,
"resetView clicked"
);
}
mUsernameView.setText(
""
);
mPasswordView.setText(
""
);
mUsernameView.requestFocus();
}
});
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package
cn.edu.wtu.junit;
import
android.app.Activity;
import
android.content.Intent;
import
android.os.Bundle;
import
android.util.Log;
import
android.widget.TextView;
public
class
HomeActivity
extends
Activity {
private
static
final
boolean
DEBUG =
true
;
private
static
final
String TAG =
"-- HomeActivity"
;
public
static
final
String EXTRA_USERNAME =
"yuan.activity.username"
;
public
static
final
String EXTRA_PASSWORD =
"yuan.activity.password"
;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
if
(DEBUG) {
Log.i(TAG,
"onCreate"
);
}
super
.onCreate(savedInstanceState);
Intent intent = getIntent();
StringBuilder sb =
new
StringBuilder();
sb.append(
"username:"
).append(intent.getStringExtra(EXTRA_USERNAME)).append(
"\n"
);
sb.append(
"password:"
).append(intent.getStringExtra(EXTRA_PASSWORD));
setContentView(R.layout.act_home);
TextView loginContentView = (TextView) findViewById(R.id.login_content);
loginContentView.setText(sb.toString());
}
}
|
然后新建一个测试工程,基于上面一个project:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
package
cn.edu.wtu.test;
import
android.app.Instrumentation;
import
android.test.ActivityInstrumentationTestCase2;
import
android.test.UiThreadTest;
import
android.test.suitebuilder.annotation.Suppress;
import
android.util.Log;
import
android.view.View;
import
cn.edu.wtu.junit.MainActivity;
public
class
MainActivityTest
extends
ActivityInstrumentationTestCase2
<mainactivity>
{
private
static
final
String TAG =
"=== MainActivityTest"
;
private
Instrumentation mInstrument;
private
MainActivity mActivity;
private
View mToLoginView;
public
MainActivityTest() {
super
(
"cn.edu.wtu.junit"
, MainActivity.
class
);
}
@Override
public
void
setUp()
throws
Exception {
super
.setUp();
mInstrument = getInstrumentation();
// 启动被测试的Activity
mActivity = getActivity();
mToLoginView = mActivity.findViewById(cn.edu.wtu.junit.R.id.to_login);
}
public
void
testPreConditions() {
// 在执行测试之前,确保程序的重要对象已被初始化
assertTrue(mToLoginView !=
null
);
}
// @UiThreadTest
// 这将会在UI线程里运行方法里所有的语句。不与UI交互的方法不允许这么做
// 注意:waitForIdleSync和sendKeys不允许在UI线程里运行
public
void
testToLogin() {
// @UiThreadTest注解使整个方法在UI线程上执行,等同于上面注解掉的代码
// exception
mInstrument.runOnMainSync(
new
Runnable() {
public
void
run() {
mToLoginView.requestFocus();
mToLoginView.performClick();
}
});
// mActivity.runOnUiThread(new Runnable(){
//
// @Override
// public void run() {
//
// mToLoginView.requestFocus();
// mToLoginView.performClick();
// }
//
// });
}
@Suppress
public
void
testNotCalled() {
// 使用了@Suppress注解的方法不会被测试
Log.i(TAG,
"method 'testNotCalled' is called"
);
}
@Override
public
void
tearDown()
throws
Exception {
super
.tearDown();
}
}
</mainactivity>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
package
cn.edu.wtu.test;
import
android.app.Instrumentation;
import
android.test.ActivityInstrumentationTestCase2;
import
android.util.Log;
import
android.view.KeyEvent;
import
android.view.View;
import
android.widget.EditText;
import
cn.edu.wtu.junit.LoginActivity;
public
class
LoginActivityTest
extends
ActivityInstrumentationTestCase2
<loginactivity>
{
private
static
final
String TAG =
"=== LoginActivityTest"
;
private
Instrumentation mInstrument;
private
LoginActivity mActivity;
private
EditText mUsernameView;
private
EditText mPasswordView;
private
View mSubmitView;
private
View mResetView;
public
LoginActivityTest() {
super
(
"cn.edu.wtu.junit"
, LoginActivity.
class
);
}
@Override
public
void
setUp()
throws
Exception {
super
.setUp();
/*
* 关闭触屏模式为了控制从测试程序中发送给模拟器或设备的按键事件,你必须关闭触屏模式。如果你不这么做,按键事件将被忽略。
* 你需要在调用getActivity()启动Activity之前调用ActivityInstrumentationTestCase2.setActivityTouchMode(false)。
* 你必须在非UI线程中运行这个调用。基于这个原因,你不能在声明有@UIThread的测试方法调用。可以在setUp()中调用。
* 要向程序发送key事件的话,必须在getActivity之前调用该方法来关闭touch模式
* 否则key事件会被忽略
*/
setActivityInitialTouchMode(
false
);
mInstrument = getInstrumentation();
mActivity = getActivity();
Log.i(TAG,
"current activity: "
+ mActivity.getClass().getName());
mUsernameView = (EditText) mActivity.findViewById(cn.edu.wtu.junit.R.id.username);
mPasswordView = (EditText) mActivity.findViewById(cn.edu.wtu.junit.R.id.password);
mSubmitView = mActivity.findViewById(cn.edu.wtu.junit.R.id.submit);
mResetView = mActivity.findViewById(cn.edu.wtu.junit.R.id.reset);
}
public
void
testPreConditions() {
assertTrue(mUsernameView !=
null
);
assertTrue(mPasswordView !=
null
);
assertTrue(mSubmitView !=
null
);
assertTrue(mResetView !=
null
);
}
public
void
testInput() {
input();
assertEquals(
"yuan"
, mUsernameView.getText().toString());
assertEquals(
"1123"
, mPasswordView.getText().toString());
}
public
void
testSubmit() {
input();
mInstrument.runOnMainSync(
new
Runnable() {
public
void
run() {
mSubmitView.requestFocus();
mSubmitView.performClick();
}
});
}
public
void
testReset() {
input();
mInstrument.runOnMainSync(
new
Runnable() {
public
void
run() {
mResetView.requestFocus();
mResetView.performClick();
}
});
assertEquals(
""
, mUsernameView.getText().toString());
assertEquals(
""
, mPasswordView.getText().toString());
}
@Override
public
void
tearDown()
throws
Exception {
super
.tearDown();
}
private
void
input() {
mActivity.runOnUiThread(
new
Runnable() {
public
void
run() {
mUsernameView.requestFocus();
}
});
// 因为测试用例运行在单独的线程上,这里最好要
// 同步application,等待其执行完后再运行
mInstrument.waitForIdleSync();
sendKeys(KeyEvent.KEYCODE_Y, KeyEvent.KEYCODE_U,
KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_N);
// 效果同上面sendKeys之前的代码
mInstrument.runOnMainSync(
new
Runnable() {
public
void
run() {
mPasswordView.requestFocus();
}
});
sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_1,
KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_3);
}
}
</loginactivity>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package
cn.edu.wtu.test;
import
android.content.Intent;
import
android.test.ActivityUnitTestCase;
import
android.widget.TextView;
import
cn.edu.wtu.junit.HomeActivity;
public
class
HomeActivityTest
extends
ActivityUnitTestCase
<homeactivity>
{
private
static
final
String TAG =
"=== HomeActivityTest"
;
private
static
final
String LOGIN_CONTENT =
"username:yuan\npassword:1123"
;
private
HomeActivity mHomeActivity;
private
TextView mLoginContentView;
public
HomeActivityTest() {
super
(HomeActivity.
class
);
}
@Override
public
void
setUp()
throws
Exception {
super
.setUp();
Intent intent =
new
Intent();
intent.putExtra(HomeActivity.EXTRA_USERNAME,
"yuan"
);
intent.putExtra(HomeActivity.EXTRA_PASSWORD,
"1123"
);
// HomeActivity有extra参数,所以我们需要以intent来启动它
mHomeActivity = launchActivityWithIntent(
"cn.edu.wtu.junit"
, HomeActivity.
class
, intent);
mLoginContentView = (TextView) mHomeActivity.findViewById(cn.edu.wtu.junit.R.id.login_content);
}
public
void
testLoginContent() {
assertEquals(LOGIN_CONTENT, mLoginContentView.getText().toString());
}
@Override
public
void
tearDown()
throws
Exception {
super
.tearDown();
}
}
</homeactivity>
|
run on android unit: