neonDion
neonDion

Reputation: 2358

Android NPE running JUnit test

I'm trying to implement some tests in my application. One thing that I want to test is writing a java object to my db, then retrieving it and asserting the the object that comes out of the db matches the object that went in.

Here's my MySQLiteHelper application code:

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;
    }
}

Here's my test class for the class above:

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 {

    }
}

When I run the test I am getting a NPE during my test. It seems to occur when the MySQLiteHelper class has its db = getWritableDatabase() line. getWriteableDatabase() is a public method from the base class.

I don't think I understand why this test results in an NPE. In my test I call the static method, MySQLiteHelper.getInstance(Context context) which should initialize the class. It is my assumption that calling getInstance will provide me with a fully initialized instance of MySQLiteHelper. Why does this not seem to be happening?

EDIT: The problem I have now is that when getWritableDatabase() is called it returns null instead of an instance of SQLiteDatabase.

Upvotes: 2

Views: 480

Answers (2)

neonDion
neonDion

Reputation: 2358

I ended completing my goals of unit testing my sqlite database. The problem seemed to be that I needed to use the build artifact called Android Instrumentation Test instead of the Unit Test build artifact.

I setup a test class in my app/src/androidTest/java directory. The test class extended InstrumentationTestCase.

When I setup my database I use the context provided by getInstrumentation().getTargetContext(). This was important because originally I tried to use getInstrumentation().getContext() and I found that that would always result in a SQLiteCantOpenDatabaseException.

So it seemed my problems occurred because: 1) I wasn't using the correct test artifact 2) I wasn't using the correct test base class 3) I wasn't getting the context correctly

Upvotes: 1

laalto
laalto

Reputation: 152917

AndroidTestCase#getContext() returns whatever Context you've set with setContext() and you haven't set anything, so a null is returned`.

Using a null context with SQLiteOpenHelper will NPE when the database is being opened e.g. with getWritableDatabase().

See Getting context in AndroidTestCase or InstrumentationTestCase in Android Studio's Unit Test feature for more details on how to set up a Contex in test cases.

Upvotes: 0

Related Questions