Android笔记---单元测试操作SQLite数据库报错:java.lang.NullPointerException

前言:

今天写了个小程序,针对SQLite数据库的相关操作写了一个单元测试类,然后发现 insert 方法一直报错报错报错: java.lang.NullPointerException。经过慢慢的调试,终于知道错误之处了,太让人忽视的一个细节。

一、呈现错误场景:

1. 代码呈现:

1) 单元测试类 TestBaseDao 部分代码:

/**
 * 测试  BaseDao 是否可用
 * @author johnnie
 *
 */
public class TestBaseDao extends AndroidTestCase {
	
	private static final String TAG = "TestBaseDao";
	private BaseDaoImpl dao = new BaseDaoImpl(getContext());
		
	public void testInsert(){	
		String table = "tb_url_down";
		ContentValues values = new ContentValues();
		values.put("url", "http://www.baidu.com");
		values.put("dirName", "baidu");
		boolean rs = this.dao.insert(table, values);
		
		if(rs){
			Log.i(TAG, "插入成功!");
		} else {
			Log.i(TAG, "插入失败!");
		}
	}
	
}

2) BaseDaoImpl 部分代码:

public class BaseDaoImpl implements BaseDao {

	public DBHelper helper;
	
	public BaseDaoImpl(Context context) {
		this.helper = new DBHelper(context);
	}
	
	@Override
	public boolean insert(String table, ContentValues values) {
		SQLiteDatabase db = this.helper.getWritableDatabase();
		long rs = db.insert(table, null, values);
		this.close(db);
		
		return (rs == 0) ? false : true;
	}
}


2. 错误信息呈现:

Android笔记---单元测试操作SQLite数据库报错:java.lang.NullPointerException_第1张图片

二、调试过程:

刚开始,我还以为我是我 insert 代码有问题,然后仔细查看了下,没发现问题。打印 dao 以及 helper 发现其值也不为 null,那么为什么会出现空指针异常呢?

然后,我将 insert() 中的逻辑抽取出来,放入单元测试类中。如下:

public void test(){
	DBHelper helper = new DBHelper(getContext());
	SQLiteDatabase db = helper.getWritableDatabase();
	String table = "tb_url_down";
	ContentValues values = new ContentValues();
	values.put("url", "http://www.baidu.com");
	values.put("dirName", "baidu");
	long rs = db.insert(table, null, values);
	Log.i(TAG, rs + "");
}
执行,结果显示:


这样的结果,让我顿时要抓狂了,这是搞莫子鬼罗?明明一样的逻辑,不同的运行结果。再次细细的对比,还是没发现问题啊!没办法,既然报错,肯定是有 bug 的,通过不断的测试,我发现,程序运行在 insert() 方法的第一行就开始报“空指针异常”了。这是什么情况?再次debug,调试代码如下:

public void test(){
	DBHelper helper = new DBHelper(getContext());
	SQLiteDatabase db = helper.getWritableDatabase();  <span style="color:#ff6666;">// 我在此处加了断点</span>
		
	<span style="color:#ff6666;">int i = 1 /0;		// 为了抛出异常以便调试,不然直接就执行完毕了</span>
		
	String table = "tb_url_down";
	ContentValues values = new ContentValues();
	values.put("url", "http://www.baidu.com");
	values.put("dirName", "baidu");
	long rs = db.insert(table, null, values);
	Log.i(TAG, rs + "");
}
还是一样的代码,仅仅是加了一行错误语句而已。debug 显示结果如下:

Android笔记---单元测试操作SQLite数据库报错:java.lang.NullPointerException_第2张图片

然后,在 debug 一下 testInsert() 方法,显示如下:


对比二者刚刚画红线的 helper,发现第二次调试的 mContext 居然是 null!这是为什么?不是应该在单元测试类 BaseDaoImpl dao = new BaseDaoImpl(getContext()); 时已经传递进去了吗?然后就着程序猿认真的态度,改了下代码试了一下,代码如下:

public void testInsert(){
	// <span style="color:#3366ff;">注意:一定注意,这个不能放在外面初始化,否则一直出现空指针错误</span>
	<span style="color:#ff0000;">dao = new BaseDaoImpl(getContext());</span>	
		
	String table = "tb_url_down";
	ContentValues values = new ContentValues();
	values.put("url", "http://www.baidu.com");
	values.put("dirName", "baidu");
	boolean rs = this.dao.insert(table, values);
		
	if(rs){
		Log.i(TAG, "插入成功!");
	} else {
		Log.i(TAG, "插入失败!");
	}
}
仅仅是增加了一行代码,然后执行结果截然不同了,运行如下:



成功了,成功了!太心酸了。被这个错误折磨好久,终于解决了,太不容易了大哭

三、总结

至此得出了一个结论,在Android单元测试时,外部普通变量的初始化,在测试方法中使用时,容易出错,且错误很隐蔽(此处dao 不为 null,dao.helper 也不为 null,但是传递到 insert 中时, helper 中的 mContext 却是 null)。因此,在编写Android测试类时,尽量在方法体内进行变量的初始化和声明。不要再体外声明普通变量,然后用于测试方法中



你可能感兴趣的:(Android笔记---单元测试操作SQLite数据库报错:java.lang.NullPointerException)