2015-10-30 82 views
2

我试图在我的应用程序中实现一些测试。我想测试正在写一个Java对象到我的数据库,然后检索,并断言说出来的数据库对象的一件事是,在去的对象匹配运行JUnit测试的Android NPE

这里是我的MySQLiteHelper应用代码:

import android.content.ContentValues; 
import android.content.Context; 
import android.database.Cursor; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteOpenHelper; 
import java.util.concurrent.atomic.AtomicInteger; 

class MySQLiteHelper extends SQLiteOpenHelper { 

    private static final String DATABASE_NAME = "unittesttester.db"; 
    private static final int DATABASE_VERSION = 8; 
    private static final String LOG_TAG = MySQLiteHelper.class.getSimpleName(); 

    private static final int WEATHER_STALENESS_PERIOD_MS = 60 * 5 * 1000; //5 minutes 

    private AtomicInteger mOpenCounter = new AtomicInteger(); 
    private static MySQLiteHelper mInstance = null; 
    private SQLiteDatabase db; 
    private Context mContext; 

    public static MySQLiteHelper getInstance(Context context) { 
     if (mInstance == null) { 
      mInstance = new MySQLiteHelper(context.getApplicationContext()); 
     } 

     return mInstance; 
    } 

    private MySQLiteHelper(Context context) { 
     super(context, DATABASE_NAME, null, DATABASE_VERSION); 
     mContext = context; 
    } 

    @Override 
    public void onCreate(SQLiteDatabase db) { 
     db.execSQL(WeatherTable.CREATE_TABLE_WEATHER); 
    } 

    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     if (newVersion <= DATABASE_VERSION) { 
      onCreate(db); 
     } 
    } 

    private synchronized SQLiteDatabase openDatabase() { 
     final int i = mOpenCounter.incrementAndGet(); 
     if (i == 1) { 
      db = getWritableDatabase(); 
     } 
     return db; 
    } 

    private synchronized void closeDatabase() { 
     final int i = mOpenCounter.decrementAndGet(); 
     if (i == 0) { 
      db.close(); 
     } 
    } 

    private void truncateWeatherTable() { 
     db = openDatabase(); 
     db.delete(WeatherTable.TABLE_WEATHER, null, null); 
     closeDatabase(); 
    } 


    public void deleteAndInsertWeather(Weather weather) { 
     db = openDatabase(); 
     db.beginTransaction(); 
     try { 
      truncateWeatherTable(); 
      insertWeather(weather); 
      db.setTransactionSuccessful(); 
     } finally { 
      db.endTransaction(); 
      closeDatabase(); 
     } 
    } 

    private void insertWeather(Weather weather) { 
     db = openDatabase(); 
     db.insert(WeatherTable.TABLE_WEATHER, null, makeWeatherCv(weather)); 
     closeDatabase(); 
    } 

    public Weather getWeather() { 
     db = openDatabase(); 
     String sql = "SELECT * FROM " + WeatherTable.TABLE_WEATHER; 
     Cursor c = null; 
     Weather weather = null; 
     try { 
      c = db.rawQuery(sql, null); 
      if (c.moveToFirst()) { 
       weather = makeWeather(c); 
       //If sample too old return null 
       if (System.currentTimeMillis() - weather.getTimestamp() > WEATHER_STALENESS_PERIOD_MS) { 
        weather = null; 
        truncateWeatherTable(); 
       } 
      } 
     } finally { 
      if (c != null) { 
       c.close(); 
      } 
      closeDatabase(); 
     } 
     return weather; 
    } 


    private Weather makeWeather(Cursor c) { 
     Weather weather = new Weather(); 
     weather.setTimestamp(c.getLong(c.getColumnIndex(WeatherTable.COLUMN_TIMESTAMP))); 
     weather.setElevation(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_ELEVATION))); 
     weather.setTemperature(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_TEMPERATURE))); 
     weather.setDusk(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_DUSK))); 
     weather.setNighttime(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_NIGHTTIME))); 
     weather.setGravity(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_GRAVITY))); 
     weather.setDaytime(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_DAYTIME))); 
     weather.setHumidity(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_HUMIDITY))); 
     weather.setPressure(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_PRESSURE))); 
     weather.setOkta(c.getDouble(c.getColumnIndex(WeatherTable.COLUMN_OKTA))); 
     weather.setDawn(c.getInt(c.getColumnIndex(WeatherTable.COLUMN_DAWN))); 
     return weather; 
    } 

    private ContentValues makeWeatherCv(Weather weather) { 
     ContentValues contentValues = new ContentValues(); 
     contentValues.put(WeatherTable.COLUMN_TIMESTAMP, weather.getTimestamp()); 
     contentValues.put(WeatherTable.COLUMN_TEMPERATURE, weather.getElevation()); 
     contentValues.put(WeatherTable.COLUMN_TEMPERATURE, weather.getTemperature()); 
     contentValues.put(WeatherTable.COLUMN_DUSK, weather.getDusk()); 
     contentValues.put(WeatherTable.COLUMN_NIGHTTIME, weather.getNighttime()); 
     contentValues.put(WeatherTable.COLUMN_GRAVITY, weather.getGravity()); 
     contentValues.put(WeatherTable.COLUMN_DAYTIME, weather.getDaytime()); 
     contentValues.put(WeatherTable.COLUMN_HUMIDITY, weather.getHumidity()); 
     contentValues.put(WeatherTable.COLUMN_PRESSURE, weather.getPressure()); 
     contentValues.put(WeatherTable.COLUMN_OKTA, weather.getOkta()); 
     contentValues.put(WeatherTable.COLUMN_DAWN, weather.getDawn()); 
     return contentValues; 
    } 
} 

这是我对上面的类测试类:

import android.test.AndroidTestCase; 
import android.test.RenamingDelegatingContext; 

import org.junit.Test; 
import static org.mockito.Mockito.*; 

public class MySQLiteHelperTest extends AndroidTestCase { 

    private MySQLiteHelper db; 
    private Weather mockedWeather = mock(Weather.class); 

    @Override 
    public void setUp() throws Exception { 
     super.setUp(); 
     context = new MockContext(); 
     setContext(context); 
     assertNotNull(context); 
     RenamingDelegatingContext renamingContext = new  RenamingDelegatingContext(getContext(), "test_"); 
     db = MySQLiteHelper.getInstance(renamingContext); 
     assertNotNull(db); 

     when(mockedWeather.getDawn()).thenReturn(0); 
     when(mockedWeather.getDaytime()).thenReturn(1); 
     when(mockedWeather.getDusk()).thenReturn(2); 
     when(mockedWeather.getElevation()).thenReturn(3.0); 
     when(mockedWeather.getGravity()).thenReturn(4.0); 
     when(mockedWeather.getHumidity()).thenReturn(5.0); 
     when(mockedWeather.getNighttime()).thenReturn(6); 
     when(mockedWeather.getOkta()).thenReturn(7.0); 
     when(mockedWeather.getPressure()).thenReturn(8.0); 
     when(mockedWeather.getTemperature()).thenReturn(9.0); 
     when(mockedWeather.getTimestamp()).thenReturn(10L); 
    } 

    @Override 
    public void tearDown() throws Exception { 
     super.tearDown(); 
    } 

    public void testGetInstance() throws Exception { 

    } 

    public void testOnCreate() throws Exception { 

    } 

    public void testOnUpgrade() throws Exception { 

    } 

    @Test 
    public void testDeleteAndInsertWeather() throws Exception { 
     db.deleteAndInsertWeather(mockedWeather); 
     Weather actualWeather = db.getWeather(); 
     assertEquals(mockedWeather.getDawn(), actualWeather.getDawn()); 
     assertEquals(mockedWeather.getDaytime(), actualWeather.getDaytime()); 
     assertEquals(mockedWeather.getDusk(), actualWeather.getDusk()); 
     assertEquals(mockedWeather.getElevation(), actualWeather.getElevation()); 
     assertEquals(mockedWeather.getGravity(), actualWeather.getGravity()); 
     assertEquals(mockedWeather.getHumidity(), actualWeather.getHumidity()); 
     assertEquals(mockedWeather.getNighttime(), actualWeather.getNighttime()); 
     assertEquals(mockedWeather.getOkta(), actualWeather.getOkta()); 
     assertEquals(mockedWeather.getPressure(), actualWeather.getPressure()); 
     assertEquals(mockedWeather.getTemperature(), actualWeather.getTemperature()); 
     assertEquals(mockedWeather.getTimestamp(), actualWeather.getTimestamp()); 

    } 

    public void testDeleteWeather() throws Exception { 

    } 

    public void testInsertWeather() throws Exception { 

    } 

    public void testGetWeather() throws Exception { 

    } 

    public void testWeatherMakeCv() throws Exception { 

    } 
} 

当我运行测试我的测试过程中,我得到一个NPE。这似乎发生在MySQLiteHelper类具有其db = getWritableDatabase()行时。 getWriteableDatabase()是来自基类的公共方法。

我不认为我明白为什么这个测试会导致NPE。在我的测试中,我调用了应该初始化类的静态方法MySQLiteHelper.getInstance(Context context)。我的假设是调用getInstance将为我提供一个完全初始化的MySQLiteHelper实例。为什么这似乎没有发生?

编辑: 我现在的问题是,当getWritableDatabase()被调用时返回NULL而不是SQLiteDatabase的实例。

+0

你为什么使用'context.getApplicationContext()'?不应该'上下文'只够? –

+0

删除getApplicationContext()并再次测试导致相同的行为。 – neonDion

+0

@MeghVidani context.getApplicationContext()确实返回null,所以你是正确的,这是问题之一。但是,在删除getApplicationContext()部分后,我仍然遇到问题。 – neonDion

回答

1

我结束了完成我的单元测试我的sqlite数据库的目标。问题似乎是我需要使用名为Android Instrumentation Test的构建工件,而不是单元测试构建工件。

我在app/src/androidTest/java目录中设置了一个测试类。测试类扩展了InstrumentationTestCase。

当我设置我的数据库时,我使用getInstrumentation()。getTargetContext()提供的上下文。这很重要,因为最初我尝试使用getInstrumentation()。getContext(),我发现这总是会导致SQLiteCantOpenDatabaseException。

看上去是如此,因为发生在我的问题: 1)我没有使用我没有使用正确的试验基地 类3正确的测试工件 2))我没有得到正确的背景下

0

AndroidTestCase#getContext()返回您已使用setContext()设置的Context,并且您没有设置任何内容,因此返回null

使用null上下文与SQLiteOpenHelper将在打开数据库时NPE,与getWritableDatabase()

有关如何在测试用例中设置Contex的更多详细信息,请参阅Getting context in AndroidTestCase or InstrumentationTestCase in Android Studio's Unit Test feature

+0

我将此代码添加到我的setUp()方法中:context = new MockContext(); setContext(context); assertNotNull(context); RenamingDelegatingContext renamingContext = new RenamingDelegatingContext(getContext(),“test_”); db = MySQLiteHelper.getInstance(renamingContext); assertNotNull(db);现在我在代码中得到一个NPE。现在,当我调用db.beginTransaction();在正在测试的方法中。 – neonDion