2.测试相关知识_打印日志_文件
测试相关知识
根据测试时是否有源代码:
- 黑盒测试:
- 白盒测试
根据测试的粒度:
- 方法测试:
- 单元测试:
- 集成测试:
- 系统测试:
根据测试的暴力程度:
- 压力测试:
- 冒烟测试:
monkey工具
用于压力测试. 首先 adb shell 进入终端中.
然后 #monkey 5000 回车.
手机屏幕就会被狂点5000次.
一个比较完整的命令:
adb shell monkey -p com.xinmei365.font -s 500 --ignore-crashes --ignore-timeouts --monitor-native-crashes -v -v 60000 > E:\java_monkey_log.txt
如何停止呢? 先进 adb shell, 然后 ps | grep monkey, 找到 monkey 的进程号, 然后 kill
进程号.
----
为什么junit一点就能运行, 没有main方法
----
Android中的单元测试
要在Android项目中运行单元测试,首先要在AndroidManifest.xml文件中加入如下配置:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"<uses-sdk android:minSdkVersion="8" /><instrumentationandroid:name="android.test.InstrumentationTestRunner"android:targetPackage="com.gaoyuan.junit" /><applicationandroid:icon="@drawable/ic_launcher"android:label="@string/app_name" ><uses-library android:name="android.test.runner" /><activity……</activity></application></manifest>
测试用例要继承 AndroidTestCase.
public class TestMyService extends AndroidTestCase {private MyService ms;/*** 测试类 TestMyService 在第一次被创建的时候 ,做些初始化工作*/@Overrideprotected void setUp() throws Exception {ms = new MyService();}/*** 测试方法,需要把异常抛给测试框架* @throws Exception*/public void testAdd() throws Exception{//MyService ms = new MyService();int result = ms.add(3, 3);assertEquals(3+3, result);}public void testSub() throws Exception {//MyService ms = new MyService();int result = ms.sub(3, 3);assertEquals(3-3, result);}/*** 测试类 TestMyService 在被销毁的时候,做清理工作*/@Overrideprotected void tearDown() throws Exception {ms = null;}}
其中,setUp,tearDown这两个方法是重写父类的方法,作用如注释所说。
另外,注意 Android 中的测试类的测试方法不需要加 @Test 注解。
除此之外, 还可以建立专门的测试工程. 创建一个Android Test Project, 勾选相应的条目即可.
自动生成的清单文件中就有那两项配置. 测试工程和被测试工程使用的是同一个 Context.
测试用例中, 将context设置为成员变量, getContext为空的问题?
1. Android测试框架的运行过程: 打包.apk, 安装到手机, 运行测试机
2. 创建AndroidTestCase对象 - 初始化成员变量 - 构造函数
3. 对象创建完成之后, 测试机会把当前应用的Context对象通过setContext()方法设置进来
4. 执行测试方法
在测试类的成员变量, 或者构造函数中, 不能调用getContext(), 因为还没设置进来, 会得到null
-------
AndroidTestRunner是干嘛的
-------
logcat的使用
logcat视图用于显示系统打印的日志. 在程序中可以使用 Log 这个类打印日志.
public class MainActivity extends Activity {
// TAG 一般为当前Activity的名字
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// log 有五种级别. TAG 表示标签, 用于指明是谁打印的信息
Log.v(TAG, "我是提醒等级的log");
Log.d(TAG, "我是调试等级的log");
Log.i(TAG, "我是信息等级的log");
Log.w(TAG, "我是警告等级的log");
Log.e(TAG, "我是错误等级的log");
// 在 android 中也可以使用 syso, 但是不会打印在控制台上, 而是logact中.
System.out.println("haha"); //System.out info
System.err.println("error"); //System.err warn
}
}
在 logcat 视图中可以选择显示不同级别的日志信息. 还可以配置过滤器,
根据应用名, TAG名, PID 等条件过滤想要的信息.
文件
保存数据到内存储设备中
用户登录案例: 当用户勾选记住密码后登陆, 将用户名密码保存到手机内存储设备中.
界面就不说了.
public class UserInfoService {// 如果一个方法没有使用到类的成员变量, 则一般将其定义为static, 这是google推荐的做法.public static boolean saveUserInfo(Context context, String username, String password) {// 应用私有的数据一般存放在 /data/data/当前应用程序包名/files/ 这个目录下// 由于不同手机目录结构可能有差异, 需使用context.getFilesDir()得到这个目录File file = new File(context.getFilesDir(), "info.txt");// 如果一个方法有返回值, 则异常一般要catch, 若没有返回值则一般往上抛.try {FileOutputStream fos = new FileOutputStream(file);fos.write((username+":"+password).getBytes());fos.close();return true;} catch (Exception e) {e.printStackTrace();return false;}}public static Map<String, String> getUserInfoMap(Context context) {try {File file = new File(context.getFilesDir(), "info.txt");FileReader fr = new FileReader(file);BufferedReader br = new BufferedReader(fr);String info = br.readLine();br.close();String[] split = info.split(":");Map<String, String> map = new HashMap<String, String>();map.put("username", split[0]);map.put("password", split[1]);return map;} catch (Exception e) {e.printStackTrace();return null;}}}
保存到SD卡中
其实和保存到手机内存几乎一样, 只不过有两点需要注意:
1. 得到路径的方式不同.
// 判断SD卡是否可用String state = Environment.getExternalStorageState();if(!Environment.MEDIA_MOUNTED.equals(state)) {Toast.makeText(this, "SD卡不可用, 请检查SD卡", Toast.LENGTH_SHORT).show();return;}// 得到SD卡路径的方法File file = new File(Environment.getExternalStorageDirectory(), "info.txt");
2. 写SD卡是需要权限的, 从4.0开始, 读SD卡也需要权限了.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
其余代码和保存到内存一模一样.
== 怎样获取可用存储空间 ==
另外几个常用API
- context.openFileOutput(String filename, int mode)
直接得到应用files目录中某个文件的输出流
- context.openFileInput(String filename)
直接得到应用files目录中某个文件的输入流
- context.getCacheDir()
获取 /data/data/当前应用包名/cache/ 目录, 用户可手动清除这个文件夹中的内容
当系统内存储不足时, 系统也会自动(但不保证)清除这个文件夹中的内容
用户和文件的访问权限
Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新
写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建
新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表
示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。如果希望文件被其他应用读和写,可以传入:
openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE +
Context.MODE_WORLD_WRITEABLE); android有一套自己的安全模型,当应用
程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文
件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package
name>/files),其他程序无法访问。除非在创建时指定了
Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE
,只有这样其他程序才能正确访问。
关于如何修改data/data下文件的权限, 参看: 修改data/data下文件的权限
ShearedPreference
Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package
name>/shared_prefs目录下
public class MainActivity extends Activity {private CheckBox cb;private SeekBar sb;private SharedPreferences sp;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);cb = (CheckBox) findViewById(R.id.cb);sb = (SeekBar) findViewById(R.id.sb);sb.setMax(100);// 通过 context 获取 SharedPreferencesp = this.getSharedPreferences("config", MODE_PRIVATE);boolean isChecked = sp.getBoolean("isChecked", false);cb.setChecked(isChecked);int progress = sp.getInt("progress", 0);sb.setProgress(progress);cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {Editor editor = sp.edit();editor.putBoolean("isChecked", isChecked);editor.commit();}});sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) { }@Overridepublic void onStartTrackingTouch(SeekBar seekBar) { }@Overridepublic void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {Editor editor = sp.edit();editor.putInt("progress", progress);editor.commit();}});}}
使用sp, 最后一定注意要 commit
.
另外,
sharedpreference也可以链式编程, sp.edit().putXXX().putXXX().apply();
这样的写法很简练
XML
生成XML
----------
使用StringBuilder, 为什么不用指定编码?
----------
public void click(View view) {try {FileOutputStream fos = this.openFileOutput("smsInfo.xml", MODE_PRIVATE);XmlSerializer ser = Xml.newSerializer();ser.setOutput(fos, "utf-8");ser.startDocument("utf-8", true);// 第一个参数是命名空间ser.startTag(null, "smss");for (SmsInfo sms : smsList) {ser.startTag(null, "sms");ser.startTag(null, "address");ser.text(sms.getAddress());ser.endTag(null, "address");ser.startTag(null, "body");ser.text(sms.getBody());ser.endTag(null, "body");ser.startTag(null, "date");ser.text(sms.getDate()+"");ser.endTag(null, "date");ser.endTag(null, "sms");}ser.endTag(null, "smss");ser.endDocument();fos.close();Toast.makeText(this, "生成xml成功", Toast.LENGTH_SHORT).show();} catch (Exception e) {e.printStackTrace();Toast.makeText(this, "生成xml异常", Toast.LENGTH_SHORT).show();}}}Pull解析XMLpublic static List<Channel> getAll(InputStream is) throws Exception {XmlPullParser parser = Xml.newPullParser();parser.setInput(is, "utf-8");List<Channel> list = null;Channel channel = null;int type = parser.getEventType();while (type != XmlPullParser.END_DOCUMENT) {switch (type) {case XmlPullParser.START_TAG:if ("weather".equals(parser.getName())) {list = new ArrayList<Channel>();} else if ("channel".equals(parser.getName())) {channel = new Channel();channel.setId(Integer.parseInt(parser.getAttributeValue(0)));} else if ("city".equals(parser.getName())) {channel.setCity(parser.nextText());} else if ("temp".equals(parser.getName())) {channel.setTemp(parser.nextText());} else if ("wind".equals(parser.getName())) {channel.setWind(parser.nextText());} else if ("pm250".equals(parser.getName())) {channel.setPm250(Integer.parseInt(parser.nextText()));}break;case XmlPullParser.END_TAG:if ("channel".equals(parser.getName())) {list.add(channel);channel = null;}break;default:break;}type = parser.next();}return list;}
浙公网安备 33010602011771号