简体   繁体   English

运行 JUnit 测试的 Android NPE

[英]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.我想测试的一件事是将一个 java 对象写入我的数据库,然后检索它并断言从数据库中出来的对象与进入的对象相匹配。

Here's my MySQLiteHelper application code:这是我的 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;
    }
}

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.当我运行测试时,我在测试期间获得了 NPE。 It seems to occur when the MySQLiteHelper class has its db = getWritableDatabase() line.当 MySQLiteHelper 类有它的 db = getWritableDatabase() 行时,似乎会发生这种情况。 getWriteableDatabase() is a public method from the base class. getWriteableDatabase() 是来自基类的公共方法。

I don't think I understand why this test results in an NPE.我想我不明白为什么这个测试会导致 NPE。 In my test I call the static method, MySQLiteHelper.getInstance(Context context) which should initialize the class.在我的测试中,我调用静态方法 MySQLiteHelper.getInstance(Context context) 来初始化类。 It is my assumption that calling getInstance will provide me with a fully initialized instance of MySQLiteHelper.我假设调用 getInstance 将为我提供一个完全初始化的 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.编辑:我现在遇到的问题是,当调用 getWritableDatabase() 时,它返回 null 而不是 SQLiteDatabase 的实例。

I ended completing my goals of unit testing my sqlite database.我完成了对我的 sqlite 数据库进行单元测试的目标。 The problem seemed to be that I needed to use the build artifact called Android Instrumentation Test instead of the Unit Test build artifact.问题似乎是我需要使用名为 Android Instrumentation Test 的构建工件而不是单元测试构建工件。

I setup a test class in my app/src/androidTest/java directory.我在我的 app/src/androidTest/java 目录中设置了一个测试类。 The test class extended InstrumentationTestCase.测试类扩展了 InstrumentationTestCase。

When I setup my database I use the context provided by getInstrumentation().getTargetContext().当我设置我的数据库时,我使用 getInstrumentation().getTargetContext() 提供的上下文。 This was important because originally I tried to use getInstrumentation().getContext() and I found that that would always result in a SQLiteCantOpenDatabaseException.这很重要,因为最初我尝试使用 getInstrumentation().getContext() 并且我发现这总是会导致 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所以我的问题似乎是因为:1)我没有使用正确的测试工件 2)我没有使用正确的测试基类 3)我没有正确地获取上下文

AndroidTestCase#getContext() returns whatever Context you've set with setContext() and you haven't set anything, so a null is returned`. AndroidTestCase#getContext()返回您使用setContext()设置的任何Context并且您没有设置任何内容,因此返回null

Using a null context with SQLiteOpenHelper will NPE when the database is being opened eg with getWritableDatabase() .使用SQLiteOpenHelpernull上下文将在数据库打开时 NPE,例如使用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.有关如何在测试用例中设置 Contex 的更多详细信息,请参阅在 AndroidTestCase 或 Android Studio 的单元测试功能中的 InstrumentationTestCase 中获取上下文

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM