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文件中加入如下配置:
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. <uses-sdk android:minSdkVersion="8" />
  3. <instrumentation
  4. android:name="android.test.InstrumentationTestRunner"
  5. android:targetPackage="com.gaoyuan.junit" />
  6. <application
  7. android:icon="@drawable/ic_launcher"
  8. android:label="@string/app_name" >
  9. <uses-library android:name="android.test.runner" />
  10. <activity
  11. ……
  12. </activity>
  13. </application>
  14. </manifest>

测试用例要继承 AndroidTestCase.
  1. public class TestMyService extends AndroidTestCase {
  2. private MyService ms;
  3. /**
  4. * 测试类 TestMyService 在第一次被创建的时候 ,做些初始化工作
  5. */
  6. @Override
  7. protected void setUp() throws Exception {
  8. ms = new MyService();
  9. }
  10. /**
  11. * 测试方法,需要把异常抛给测试框架
  12. * @throws Exception
  13. */
  14. public void testAdd() throws Exception{
  15. //MyService ms = new MyService();
  16. int result = ms.add(3, 3);
  17. assertEquals(3+3, result);
  18. }
  19. public void testSub() throws Exception {
  20. //MyService ms = new MyService();
  21. int result = ms.sub(3, 3);
  22. assertEquals(3-3, result);
  23. }
  24. /**
  25. * 测试类 TestMyService 在被销毁的时候,做清理工作
  26. */
  27. @Override
  28. protected void tearDown() throws Exception {
  29. ms = null;
  30. }
  31. }

其中,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 等条件过滤想要的信息.

文件

保存数据到内存储设备中

用户登录案例: 当用户勾选记住密码后登陆, 将用户名密码保存到手机内存储设备中.
界面就不说了.

  1. public class UserInfoService {
  2. // 如果一个方法没有使用到类的成员变量, 则一般将其定义为static, 这是google推荐的做法.
  3. public static boolean saveUserInfo(Context context, String username, String password) {
  4. // 应用私有的数据一般存放在 /data/data/当前应用程序包名/files/ 这个目录下
  5. // 由于不同手机目录结构可能有差异, 需使用context.getFilesDir()得到这个目录
  6. File file = new File(context.getFilesDir(), "info.txt");
  7. // 如果一个方法有返回值, 则异常一般要catch, 若没有返回值则一般往上抛.
  8. try {
  9. FileOutputStream fos = new FileOutputStream(file);
  10. fos.write((username+":"+password).getBytes());
  11. fos.close();
  12. return true;
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. return false;
  16. }
  17. }
  18. public static Map<String, String> getUserInfoMap(Context context) {
  19. try {
  20. File file = new File(context.getFilesDir(), "info.txt");
  21. FileReader fr = new FileReader(file);
  22. BufferedReader br = new BufferedReader(fr);
  23. String info = br.readLine();
  24. br.close();
  25. String[] split = info.split(":");
  26. Map<String, String> map = new HashMap<String, String>();
  27. map.put("username", split[0]);
  28. map.put("password", split[1]);
  29. return map;
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. return null;
  33. }
  34. }
  35. }

保存到SD卡中

其实和保存到手机内存几乎一样, 只不过有两点需要注意:
1. 得到路径的方式不同.
  1. // 判断SD卡是否可用
  2. String state = Environment.getExternalStorageState();
  3. if(!Environment.MEDIA_MOUNTED.equals(state)) {
  4. Toast.makeText(this, "SD卡不可用, 请检查SD卡", Toast.LENGTH_SHORT).show();
  5. return;
  6. }
  7. // 得到SD卡路径的方法
  8. File file = new File(Environment.getExternalStorageDirectory(), "info.txt");

 
2. 写SD卡是需要权限的, 从4.0开始, 读SD卡也需要权限了.
  
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  2. <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目录下
  1. public class MainActivity extends Activity {
  2. private CheckBox cb;
  3. private SeekBar sb;
  4. private SharedPreferences sp;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. cb = (CheckBox) findViewById(R.id.cb);
  10. sb = (SeekBar) findViewById(R.id.sb);
  11. sb.setMax(100);
  12. // 通过 context 获取 SharedPreference
  13. sp = this.getSharedPreferences("config", MODE_PRIVATE);
  14. boolean isChecked = sp.getBoolean("isChecked", false);
  15. cb.setChecked(isChecked);
  16. int progress = sp.getInt("progress", 0);
  17. sb.setProgress(progress);
  18. cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
  19. @Override
  20. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
  21. Editor editor = sp.edit();
  22. editor.putBoolean("isChecked", isChecked);
  23. editor.commit();
  24. }
  25. });
  26. sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
  27. @Override
  28. public void onStopTrackingTouch(SeekBar seekBar) { }
  29. @Override
  30. public void onStartTrackingTouch(SeekBar seekBar) { }
  31. @Override
  32. public void onProgressChanged(SeekBar seekBar, int progress,
  33. boolean fromUser) {
  34. Editor editor = sp.edit();
  35. editor.putInt("progress", progress);
  36. editor.commit();
  37. }
  38. });
  39. }
  40. }

使用sp, 最后一定注意要 commit .
另外, sharedpreference也可以链式编程, sp.edit().putXXX().putXXX().apply(); 这样的写法很简练
 

XML

生成XML

----------
使用StringBuilder, 为什么不用指定编码?
----------
    
  1. public void click(View view) {
  2. try {
  3. FileOutputStream fos = this.openFileOutput("smsInfo.xml", MODE_PRIVATE);
  4. XmlSerializer ser = Xml.newSerializer();
  5. ser.setOutput(fos, "utf-8");
  6. ser.startDocument("utf-8", true);
  7. // 第一个参数是命名空间
  8. ser.startTag(null, "smss");
  9. for (SmsInfo sms : smsList) {
  10. ser.startTag(null, "sms");
  11. ser.startTag(null, "address");
  12. ser.text(sms.getAddress());
  13. ser.endTag(null, "address");
  14. ser.startTag(null, "body");
  15. ser.text(sms.getBody());
  16. ser.endTag(null, "body");
  17. ser.startTag(null, "date");
  18. ser.text(sms.getDate()+"");
  19. ser.endTag(null, "date");
  20. ser.endTag(null, "sms");
  21. }
  22. ser.endTag(null, "smss");
  23. ser.endDocument();
  24. fos.close();
  25. Toast.makeText(this, "生成xml成功", Toast.LENGTH_SHORT).show();
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. Toast.makeText(this, "生成xml异常", Toast.LENGTH_SHORT).show();
  29. }
  30. }
  31. }
  32. Pull解析XML
  33. public static List<Channel> getAll(InputStream is) throws Exception {
  34. XmlPullParser parser = Xml.newPullParser();
  35. parser.setInput(is, "utf-8");
  36. List<Channel> list = null;
  37. Channel channel = null;
  38. int type = parser.getEventType();
  39. while (type != XmlPullParser.END_DOCUMENT) {
  40. switch (type) {
  41. case XmlPullParser.START_TAG:
  42. if ("weather".equals(parser.getName())) {
  43. list = new ArrayList<Channel>();
  44. } else if ("channel".equals(parser.getName())) {
  45. channel = new Channel();
  46. channel.setId(Integer.parseInt(parser.getAttributeValue(0)));
  47. } else if ("city".equals(parser.getName())) {
  48. channel.setCity(parser.nextText());
  49. } else if ("temp".equals(parser.getName())) {
  50. channel.setTemp(parser.nextText());
  51. } else if ("wind".equals(parser.getName())) {
  52. channel.setWind(parser.nextText());
  53. } else if ("pm250".equals(parser.getName())) {
  54. channel.setPm250(Integer.parseInt(parser.nextText()));
  55. }
  56. break;
  57. case XmlPullParser.END_TAG:
  58. if ("channel".equals(parser.getName())) {
  59. list.add(channel);
  60. channel = null;
  61. }
  62. break;
  63. default:
  64. break;
  65. }
  66. type = parser.next();
  67. }
  68. return list;
  69. }

 




posted @ 2016-09-30 23:06  杨伟乔  阅读(359)  评论(0编辑  收藏  举报