I am trying to write test cases for my app database. This is what I do in the setup method:
#Before
public void testCaseSetUp() {
RenamingDelegatingContext context = new RenamingDelegatingContext(getTargetContext(), "test_");
dbController = new DBController(context);
dbController.open();
}
DBController.java
public DBController(Context c) {
context = c;
}
public DBController open() throws SQLException {
dbHelper = DBHelper.getInstance(context);
database = dbHelper.getWritableDatabase();
return this;
}
RenamingDelegatingContext does not create a new test database instead uses the existing db file. This is causing my test cases to fail as I already have data.
Are you sure that you recreate your SQLiteOpenHelper somewhere inside your DBHelper class? From the looks of it the latter is a singleton, so I can assume you're not recreating it every time. You should do so in your #Before method, because the idea behind using a RenamingDelegatingContext here is that it deletes old databases before opening a new one.
Also it's a good practice to close the SQLiteOpenHelper class after your tests. Something like:
#After
public void tearDown() {
dbHelper.close();
}
If this doesn't work, please post your DBHelper class to verify it's ok.
Related
I do not understand what exactly should I do in order to get a clean and a different db from the one that the app uses.
This is my test class:
public class SQLTest extends ProviderTestCase2{
private static String testDbPrefix = "unitTest_";
public SQLTest (){
super(MyContentProvider.class, MyContract.CONTENT_AUTHORITY);
}
#Override
#Before
public void setUp() throws Exception {
//setContext(InstrumentationRegistry.getTargetContext());
RenamingDelegatingContext context = new RenamingDelegatingContext(InstrumentationRegistry.getTargetContext(), testDbPrefix);
setContext(context);
super.setUp();
}
#Test
public void test1(){
//test logic
}
}
I noticed that it always runs on the db that the app uses, even though I'm using a both ProviderTestCase2 and RenamingDelegatingContext, which are supposed to ensure I'm running with a clean db.
Can anyone explain please what am I missing???
Thanks in advance!
I am trying to write test cases for my database.
I have a helper class that extends to SQLiteOpenHelper
DBHelper.java
public DBHelper(Context context) {
super(context, DBConstants.DATABASE_NAME, null, DBConstants.DATABASE_VERSION);
}
and a constructor class that has all the inserts deletes etc.
DBController.java
public DBController open() throws SQLException {
dbHelper = DBHelper.getInstance(context);
database = dbHelper.getWritableDatabase();
return this;
}
my test class
DBControllerTest.java
#Mock
Context mContext;
DBController dbController;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
RenamingDelegatingContext context = new RenamingDelegatingContext(mContext, "test_");
dbController = new DBController(context);
dbController.open();
}
Here when i do dbController.open(), the dbHelper.getWritableDatabase() always returns null.
How do i solve this problem. Also am I mocking it the right way. I have searched this a lot but did not find a solution. What is the best way to test database queries.
You can't mock Context like that, you need to use the instrumentation's Context. Since this test requires Android code and therefore instrumentation, make sure you put it in your test in the androidTest directory.
See this answer for an example.
I am working on an Android app that uses OrmLiteSqliteOpenHelper to connect to the SQLite db.
public class MyDatabaseHelper extends OrmLiteSqliteOpenHelper {
...
private static final String DATABASE_NAME = "mydb.sqlite";
private static MyDatabaseHelper helper = null;
private MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, BuildConfig.DATABASE_VERSION);
}
public static synchronized MyDatabaseHelper getHelper(Context context) {
if (helper == null) {
helper = new MyDatabaseHelper(context);
}
return helper;
}
}
To fetch data from the db, I have some provider classes, they use some DAO.
public class ProductsProvider {
public static List<Products> getProducts(Context context) {
MyDatabaseHelper helper = MyDatabaseHelper.getHelper(context);
Dao<Product, String> daoProducts = helperDatabase.getProductDao();
...
...
...
}
}
I have in place Robolectric to test my code, but I am having hard time to understand how to use together Robolectric with ORMLite.
My idea is to have a mock database.sqlite, prefilled with the same structure and data I normally have, and use that for all my tests. For example, if I want to test the ProductsProvider class, I should do:
#RunWith(MyTestRunner.class)
public class ProductsProviderTest extends MyTestCase {
#Test
public void testDb() {
List<Products> products = ProductsProvider.getProducts(getTestContext());
assertNotNull(products);
assertFalse(products.isEmpty());
}
}
Notice that ProductsProvider.getProducts() will use MyDatabaseHelper, which will use the db in the standard location of the Android app, and not my local file.
How can modify my code for the tests to have the tests using a local db added as an asset or a resource, without touching the real code of the app?
For anyone who can't stand the 6-12-second startup times of Robolectric 3 + Gradle, the following works with Android's built-in JUnit4 tests (DbHelper extends OrmLiteSqliteOpenHelper).
#RunWith(AndroidJUnit4.class)
public class DbTest {
private DbHelper dbHelper;
#Before
public void setUp() throws Exception {
Context context = new RenamingDelegatingContext(
InstrumentationRegistry.getTargetContext(), "test_");
dbHelper = new DbHelper(context);
}
}
There are probably many optimisations you could make, but I can run a small, I/O-heavy suite that wipes the DB each time on GenyMotion in 2.5 seconds.
If you haven't been keeping up-to-date with it, I definitely recommend checking out what Google has done with its testing features recently. The intro on the Robolectric site is a flat-out lie at this point.
Here how we are doing it.
We put sample DB file in src/test/res folder
We run next code before DB test:
private void copyTestDatabase( String resourceDBName )
throws URISyntaxException, IOException
{
String filePath = getClass().getResource( resourceDBName ).toURI().getPath();
String destinationPath = new ContextWrapper( Robolectric.application.getApplicationContext() ).getDatabasePath(
DatabaseHelper.DATABASE_NAME ).getAbsolutePath();
Files.copy( new File( filePath ), new File( destinationPath ) );
}
Be careful since these tests are super long
I created a SQlite DB. It is no problem to write or read from my DB. In my DBHandler.class I added some methods to read one column for example. Every method has dbHandler.close() line. If I want to call a method from the DBHandler.class, so I have to make something like:
... DBHandler db = new DBHandler(this); ... list = db.getAllBlock()
Shoud I put the line
db.close()
after
list...
?????
That is my problem to understand.... Thank you!
It's inefficient to constantly have to reconnect to the database everytime you want to do something, so remove the close() statements from each line and create a single instance of the DB Handler class:
It's much better to have a single DBHandler
DBHandler db;
public Activity/Class Constructor()
{
DBHandler db = new DBHandler(this);
}
private void DoSomethingWithDatabase()
{
db.doSomethingHere();
}
private void DoSomethingWithDatabaseAgain()
{
db.doSomethingThere();
}
public void Activity/ClassClose()
{
db.CloseDatabase();
}
instance inside which ever Activity/Class is using it, then have a close method in DBHandler, which closes the connection to the database after the activity/class is finished using it.
public class DBHandler
{
private static SQLiteDatabase m_DB;
public DBHandler open() throws android.database.SQLException
{
m_DB = m_DBHelper.getWritableDatabase();
}
public void doSomethingHere()
{
m_DB.getAllBlock()
}
public void doSomethingThere()
{
m_DB.getAllSomethingElse()
}
public void CloseDatabase()
{
m_DB.close();
}
}
In fact, it's even better to have the DBHandler as a Singleton then you can have app wide database access without the inefficient overheads of re-establishing connections every time you want it.
Exception:
CREATE TABLE android_metadata failed
Failed to setLocale() when constructing, closing the database
android.database.sqlite.SQLiteException: database is locked
My app works fine and has no db issues, except when onUpgrade() is called.
When onUpgrade is automatically called, it tries to use the CarManager class below to do data manipulation required for the upgrade. This fails because the db is locked.
Because this seems like it should be a normal thing to do, it seems that I must not be structuring the following code correctly (two classes follow, a helper and a table manager):
public class DbHelper extends SQLiteOpenHelper {
private Context context;
//Required constructor
public DbAdapter(Context context)
{
super(context, "my_db_name", null, NEWER_DB_VERSION);
this.context = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
overrideDB = db;
CarManager.migrateDataForOnUpgrade(context);
}
}
public class CarManager {
DbHelper dbHelper;
public CarManager(Context context)
{
dbHelper = new DbHelper(context);
}
public void addCar(String make, String model)
{
ContentValues contentValues = new ContentValues();
contentValues.put("make", make);
contentValues.put("model", model);
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.insert("car", null, contentValues);
db.close();
}
public static void migrateDataForOnUpgrade()
{
//Code here that migrates data when onUpgrade() is called
//Db lock happens here
}
}
Any ideas?
Do people set up table manager (ex: dao) differently than this?
edit: I talked to the google team # android developer hours, and they said onUpgrade3 was never meant to do anything like structural changes (alters). So yes, it seems like there are some hacks that must be used in many instances right now.
I use the following model by extending the Application class. I maintain a single static instance of my db helper which all other app components use...
public class MyApp extends Application {
protected static MyAppHelper appHelper = null;
protected static MyDbHelper dbHelper = null;
#Override
protected void onCreate() {
super.onCreate();
...
appHelper = new MyAppHelper(this);
dbHelper = MyAppHelper.createDbHelper();
dbHelper.getReadableDatabase(); // Trigger creation or upgrading of the database
...
}
}
From then on any class which needs to use the db helper simply does the following...
if (MyApp.dbHelper == null)
MyApp.appHelper.createDbHelper(...);
// Code here to use MyApp.dbHelper