1

I would like to do some Unit test in Android for SQLiteOpenHelper using Robolelectric. However, I can't create the database as I can't get the context. I tried out 4 options that are mentioned here How can we access context of an application in Robolectric? but none of them worked. Here you see the code of the testclass and in the "setUp" method you see the different options that I tried with the corresponding error message after //

package com.example.td.barapp;

import android.content.Context;
import android.database.Cursor;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;


@RunWith(RobolectricTestRunner.class)


public class DataBaseHelperTest {

    private DataBaseHelper helperDB;


    @Before
    public void setUp() throws Exception {
        // Context context = ShadowApplication.getInstance().applicationContext; //Error: Cannot resolve symbol 'applicationContext'
        //Context context = Robolectric.application; // Error: Cannot resolve symbol application
        //Context context = ApplicationProvider.getApplicationContext(); // Error: Cannot resolve symbol 'ApplicationProvider'
        //Context context = RuntimeEnvironment.systemContext; // Error: Cannot resolve symbol 'systemContext'
//Context context = getClass().getClassLoader(); // Error: Required type: Context Provided: ClassLoader
        Context context = InstrumentationRegistry.getInstrumentation().getContext(); // Error: Cannot resolve symbol 'InstrumentationRegistry'
        helperDB = new DataBaseHelper(context);
    }

    @After
    public void tearDown() {
        helperDB.close();
    }

    @Test
    public void insertDataDB_TableRatings() {
        boolean insertRating = helperDB.insertDataDB_TableRatings("Orange Juice", "Any", "Any", 1,
                0,0,1,0,0 );

        Assert.assertTrue(insertRating);
    }

    @Test
    public void getDataDB_TableRating() {
        String drinkName = "Orange Juice";
        String drinkStrength = "Any";
        String drinkSize = "Any";

        boolean insertRating = helperDB.insertDataDB_TableRatings(drinkName, drinkStrength, drinkSize, 1,
                0,0,1,0,0 );


        Cursor res = helperDB.getDataDB_TableRating(drinkName, drinkStrength, drinkSize);
        Assert.assertFalse(res.getCount()==0);

        res.moveToFirst();
        Assert.assertEquals (res.getString(0), drinkName);
        Assert.assertEquals (res.getString(6), "1");

        drinkSize = "Large";

        boolean insertRating2 = helperDB.insertDataDB_TableRatings(drinkName, drinkStrength, drinkSize, 1,
                0,0,0,1,0 );

        res = helperDB.getDataDB_TableRating(drinkName, drinkStrength, drinkSize);
        Assert.assertFalse(res.getCount()==0);

        res.moveToFirst();
        Assert.assertEquals (res.getString(2), drinkName);
        Assert.assertEquals (res.getString(7), "1");


    }

    @Test
    public void updateDataDB_TableRatings() {
        String drinkName = "Orange Juice";
        String drinkStrength = "Any";
        String drinkSize = "Any";

        boolean insertRating = helperDB.insertDataDB_TableRatings(drinkName, drinkStrength, drinkSize, 1, 0,0,1,0,0 );
        boolean valueUpdated = helperDB.updateDataDB_TableRatings(drinkName, drinkStrength , drinkSize, 2, 0, 0, 0, 1, 0);

        Cursor res = helperDB.getDataDB_TableRating(drinkName, drinkStrength, drinkSize);
        Assert.assertFalse(res.getCount()==0);
        res.moveToFirst();
        Assert.assertEquals (res.getString(8), "1");
    }

    @Test
    public void deleteDataDB_TableRatings() {
        String drinkName = "Orange Juice";
        String drinkStrength = "Any";
        String drinkSize = "Large";

        boolean insertRating = helperDB.insertDataDB_TableRatings(drinkName, drinkStrength, drinkSize, 1, 0,0,1,0,0 );
        helperDB.deleteDataDB_TableRatings (drinkName, drinkStrength, drinkSize);
    }
}

I also added the following lines in my build.gradle file:

testImplementation 'junit:junit:4.13'
testImplementation 'org.robolectric:robolectric:3.0'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

Would you mind telling me what mistake I am making? I'd appreciate every comment.

Update: As it seems that I might be missing some dependencies I uploaded the build.gradle file with the dependencies. Would you mind telling me what the mistake is and why I can't get any context with Roblectric?

apply plugin: 'com.android.application'
apply plugin: "androidx.navigation.safeargs"


android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.td.barapp"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        viewBinding {
            enabled = true
        }
        dataBinding {
            enabled=true
        }

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    def nav_version = "2.3.0"
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.2.0-alpha06'
    testImplementation 'junit:junit:4.13'
    testImplementation 'org.robolectric:robolectric:3.0'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    androidTestImplementation 'android.arch.core:core-testing:1.2.1'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation "androidx.recyclerview:recyclerview:1.1.0"
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-ui:$nav_version"


}
VanessaF
  • 515
  • 11
  • 36

1 Answers1

0

I am pretty sure that you cannot test SQLite with robolectric. My room tests, for instance, only run in AndroidTest/Instrumented. If you are willing to change to Room instead of using SQLite directly, its testing is well documented.

If you want to learn more about room there is this code lab.

And if you wanna learn more about its testing you can check it here.

Henrique Vasconcellos
  • 1,144
  • 1
  • 8
  • 13
  • Thanks Henrique for your answer. Basically I found several webpages that claimed that you can infact test SQLite with robolectric, like https://medium.com/mobile-app-development-publication/android-sqlite-database-unit-testing-is-easy-a09994701162 or https://github.com/robolectric/robolectric/blob/master/robolectric/src/test/java/org/robolectric/shadows/SQLiteOpenHelperTest.java. – VanessaF Sep 24 '20 at 12:48
  • But anyways, I have somehow the empression that testing the database with Unit test for my application is not worth the effort at all. I can just test the database by looking into the changes of the database. This way I do not have to implement all those test that are not worth the time according to my point of view – VanessaF Sep 24 '20 at 12:49
  • And I do not want to use Room instead of SQLiteOpenHelper because I just managed to learn about SQLiteOpenHelper and do not want to learn something new all the time. – VanessaF Sep 24 '20 at 12:50
  • Hey, I totally understand, learning new things all the time can be very time consuming. But I honestly suggest that you put on your todolist to learn it.. it is really easy, clean and easy to test. If you are using dagger somehow, I have this repo that may help you. https://github.com/chenriquevz/DesafioOrama – Henrique Vasconcellos Sep 24 '20 at 13:26
  • By the way, can you update your question with the log from the crash/error? – Henrique Vasconcellos Sep 24 '20 at 13:28
  • Thanks Henrique for your answer and effort. Basiically I have already put the error messges into the code after the //. I could not compile the code because of these error messges. This is why I can't post the log of an error because the app could not be started. – VanessaF Sep 24 '20 at 13:39
  • Thanks also for your comment regarding the Room library. Maybe one day I will learn it but at the moment I do not want to do it and for me I do not see the big advantage of doing it. It is claimed that it can gurantee compiler safety of the SQL code. This is not so important for me as I can get on quite well with SQLiteOpenHelper. At the end of the day it is just standard SQL code, nothing more and nothing less. There is no need to increase the complexity by adding additional layers above it. – VanessaF Sep 24 '20 at 13:41
  • One last chance to help you. Have you tried with "getClass().getClassLoader()" And "InstrumentationRegistry.getInstrumentation().getContext()" – Henrique Vasconcellos Sep 24 '20 at 14:08
  • Thanks Henrique for your answers and effort. I just tried your two suggestions without success. You can see the error messages in the updated code (I added your suggested lines). Thanks for your help. I really appreciate it. – VanessaF Sep 24 '20 at 14:36
  • Thanks Henrique for your comments. You mentioned in the very beginning that you think that Robolectric is not used for testing databases and stuff like this. As far as I read Robolectric is especially used to test applications where you need a context because normal 'local' unit test are aimed at testing methods that do not need a context (a device running). What do you use Robolectric mainly for? What do you test with it? – VanessaF Sep 24 '20 at 14:40
  • Hello, Vanessa. You can have context in a unit test even without Robo, for instance in the repo I posted earlier you will find a unit retrofit test with context but doesnt use retrofit. I mainly use robolectric to run "instrumental" unit tests, or better saying, I try to write tests that I can run with an emulator or without. For instance: https://medium.com/google-developer-experts/pushing-the-limits-of-androidx-test-3776ff249c71 – Henrique Vasconcellos Sep 24 '20 at 20:40
  • Let me tell you, check if you have this dependency: testImplementation "android.arch.core:core-testing:1.2.1" – Henrique Vasconcellos Sep 24 '20 at 20:41
  • Hi Henrique, thank you for your answers and effort. I really appreciate your help. I tried to insert your code in 3 different ways: androidTestImplementation ' ', testImplementation ' ', implementation ' '. None of them had any effect. The errors still remain exactly the same. – VanessaF Sep 25 '20 at 07:29
  • Regarding Robolectric: Did I understand correctly that 'instrumental' tests are just unit test with an Emulator or a device running (but the device and emulator do not necessariliy have to be online, it can be run internally by Robolectric)? So in this case you need a context, right? – VanessaF Sep 25 '20 at 07:32
  • If you clone the repo that I sent you earlier, you will see that you will be able to get context in several ways. I think you are missing some dependency.. By default, you have the AndroidTest folder and test, right? With roboelectric it is possible to run most of the AndroidTest folder without a device/emulator. If it is just a matter of getting a context, the "getClass().getClassLoader()" should do it for you. – Henrique Vasconcellos Sep 25 '20 at 14:38
  • Thanks Henrique again for your comment and effort. I uploaded the build.gradle file as you said that there might be a mistake in it. Maybe you can have a look at it. – VanessaF Sep 26 '20 at 07:35
  • Regarding the test folders. Yes, by default you have the AndroidTest and test folder. I tried to copy the code of the testclass (which you can see in my question) in both folders. Normally it was in the test folder and there I get the error messages shown above. When copying it into the AndroidTest folder I additionally get the error message "Cannot resolve symbol 'RobolectricTestRunner'" – VanessaF Sep 26 '20 at 07:35
  • Thanks Henrique for your comments and efforts. So you do not have any other idea why it is not working? Did you also not see a mistake in the dependecies? So I just can't get the context. Do you think that you can't get the context in general with Robolectric? – VanessaF Sep 28 '20 at 20:20
  • Hi, sorry for the delay. I think you are missing some dependencies, as I said, check all dependencies in this project github.com/chenriquevz/DesafioOrama you should be able to use some of the methods to get context if you use those dependencies. It is totally possible to get context with robolectric! – Henrique Vasconcellos Sep 29 '20 at 16:34