现在的位置: 首页 > 综合 > 正文

Android测试驱动开发实践2

2013年10月30日 ⁄ 综合 ⁄ 共 4044字 ⁄ 字号 评论关闭

在实际项目开发过程中,一般先实现核心功能,最后再做辅助性功能,这样可以尽快验证Idea的正确性,同时有助于让老板、投资人或客户看到可运行的产品,从而对产品充满信心,加大对项目的支持。

但是对于我们这个项目而言,我们首先需要得到一个Android应用MVC的架构体系,因此我们首先来实现一些典型功能,但是可以完整体现MVC架构的功能。在此我们选择任何应用程序在启动时都会显示的Splash页面,通常这个页面会显示一个应用图片,过30秒左右再显示程序的主界面,应用在这段时间完成数据加载等准备工作。

在这里我们要稍微背离一下测试驱动开发的标准方法,原因是我们在进行Android应用开发,由于Android系统限制有很多方面是很难做单元测试的,硬做单元测试,除了理论上的有效性外,没有任何实际意义。

在这里,我们采用验收测试驱动开发的理念,即我们开发足够功能来满足一个验收测试用例。这里我们选择的一个验收测试用例为:应用在开启时,先显示10秒应用图片,然后自动进入应用首页,也就是我们通常所看到的Splash屏幕功能。

我们首先定义SplashActivity类,代码如下所示:

package com.bjcic.wkj;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;

public class SplashActivity extends Activity {
	// 生命周期方法---开始
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		 //全屏
		setContentView(R.layout.splash);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        appModel = (AppModel)getApplication();
        appController = appModel.getAppController();
        appController.postDelayed(new Runnable() {
        	/**
        	 * 隔10秒钟启动主页面
        	 */
        	@Override
        	public void run() {
        		appController.processEvent(new AppEvent(SplashActivity.this, AppEvent.EVE_SPLASH_END, null));
        	}
        }, AppKeys.SPLASH_DURATION);
        // 启动异步任务准备应用数据
	}
	// 生命周期方法---结束
	
	private AppController appController = null;
	private AppModel appModel = null;
}

这个Activity所对应的布局文件为:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:background="@drawable/splash">
    

</LinearLayout>

布局文件就是定义了一下Splash屏幕的背景图。

这里我们引入了AppController类,是应用的控制器类。Activity中用户的操作和系统的状态改变都会生成相应的事件,由AppController.processEvent来进行统一处理,同时异步任务、线程等产生的需要界面更新的操作,通过向AppController发送Message来实现(因为AppController继承了Handler类)。具体代码如下所示:

package com.bjcic.wkj;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class AppController extends Handler {
	public AppController(AppModel appModel) {
		super();
		this.appModel = appModel;
	}
	
	/**
	 * Activity中会根据用户的操作或系统状态,产生对应的事件,发送给AppController进行统一处理。
	 * @param event
	 */
	public void processEvent(AppEvent event) {
		switch (event.getEventId()) {
		case AppEvent.EVE_SPLASH_END: // 从Splash界面显示主界面
			showMainActivity((Activity)event.getContext(), event.getParams());
			break;
		default:
			break;
		}
	}
	
	/**
	 * 异步任务、线程、后台服务等需要更新界面时,向AppController发送消息即可
	 */
	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
	}
	
	/**
	 * 关闭Splash页面并打开应用主界面
	 * @param activity
	 * @param params
	 */
	private void showMainActivity(Activity activity, Bundle params) {
		Log.d("wkj", "activity=" + activity + "; c=" + MainActivity.class + "!");
		Intent intent = new Intent(activity, MainActivity.class);
		activity.startActivity(intent);
		activity.finish();
	}
	
	private AppModel appModel = null;
}

在上面的代码中,事件处理函数直接写在的应用总的Controller中,其实也可以写到具体的Controller中,为了代码的可维护性,最好还是将事件处理写到对应模块的Controller中比较好。

下面就是AppEvent的定义:

package com.bjcic.wkj;

import android.content.Context;
import android.os.Bundle;

public class AppEvent {
	public AppEvent(Context context, int eventId, Bundle params) {
		this.context = context;
		this.eventId = eventId;
		this.params = params;
	}
	
	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}

	public Bundle getParams() {
		return params;
	}
	
	
	public int getEventId() {
		return eventId;
	}

	public void setEventId(int eventId) {
		this.eventId = eventId;
	}


	public final static int EVE_NONE = 0;
	public final static int EVE_SPLASH_END = 1; // Splash界面显示时间到期
	
	private Context context = null;
	private int eventId = 0;
	private Bundle params = null;
}

在上面的事件定义中,事件中包含当前的Activity,事件ID和事件参数,这样AppController就可以直接对事件进行处理了。

最后,我们在Splash页面停留10秒,这里需要定义一个常量,我们将应用中需要用到的重要常量,统一定义到AppKeys中,如下所示:

package com.bjcic.wkj;

public class AppKeys {
	public final static long SPLASH_DURATION = 10 * 1000;
}

好的,现在可以运行这个应用程序了,如果一切正常,应该可以看到一个Splash页面显示10秒钟后,进入到程序主界面中。至此我们的第一个验收测试用例就顺利通过了。

这时,我们再回到WkjTest这个工程中,以Android Junit形式运行MainActivityTest,这时应该显示所有测试用例全部通过。

注:大家也许注意到了,测试驱动开发是以一小步一小步的开发测试为基础的,在实际工作中,有一半愉上的程序员喜欢先把所有代码写好,然后在进行调试。当然也有一部分开发人员写一点调一点,这纯属于习惯性问题,不存在孰优孰劣的问题。但是采用测试驱动开发方法学,就要采用后面的工作方式。

因此,测试驱动开发不一定适合所有人,对于喜欢一次性先把代码写好,然后进行调试的人来说,让他们接受测试驱动开发的工作方式是很困难的,这一点希望大家能够重视起来。

抱歉!评论已关闭.