Use mock db when testing with Robolectric and ORMLite - android

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

Related

How to clean db with ProviderTestCase2 or RenamingDelegatingContext

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!

Writing SQLiteOpenHelper test cases in JUnit4 and Mockito

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.

RenamingDelegatingContext not creating a test database

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.

Use local db when testing with Robolectric 3 and ORMLite

I am working on an Android app that uses OrmLite to connect to the SQLite db.
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
...
private static final String DATABASE_NAME = "db.sqlite";
private static DatabaseHelper helper = null;
private DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, BuildConfig.DATABASE_VERSION);
}
public static synchronized DatabaseHelper getHelper(Context context) {
if (helper == null) {
helper = new DatabaseHelper(context);
}
return helper;
}
}
To fetch data from the db, I have some helper classes, they use some DAO.
public class AccountsDBHelper {
public List<Account> getAllAccounts(Context context) {
DatabaseHelper dbHelper = DatabaseHelper.getHelper(context);
Dao<Acount, Integer> daoAccounts = dbHelper.getAccountsDao();
...
...
...
}
}
I have in place Robolectric 3 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 in assets, following the same structure as the one I have in production.
This database will be prefilled with data from test accounts, 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 AccountsDBHelperTest {
#Test
public void testGetAllAccounts() {
List<Accounts> accounts= AccountsDBHelper.getAllAccounts(getTestContext());
assertNotNull(accounts);
assertFalse(accounts.isEmpty());
}
}
Notice that AccountsDBHelper.getAllAccounts() will use the DatabaseHelper, which will use the db in the Android app assets, and not my local production database file. How can I modify my code of the tests to have them using a local db added as an asset ? Without touching the real code of the app? Any help will be very welcome, thank you...
The missing link was to point to the path of the the local database
String dbPath = RuntimeEnvironment.application.getPackageResourcePath() + DB_FOLDER + dbName;

call dao in another for sqlite transaction

i have 2 dao and i need to call method between them
I did something like this but it s really bad because it does a infinite loop and it's does not respect quality standard
What is the proper way to do this please ?
Thank you very much
DAO 1
public class InfractionDAO {
private SQLiteDatabase database;
private SqLiteManager dbHelper;
private OffenderDAO offenderDAO;
public InfractionDAO(Context context) {
dbHelper = new SqLiteManager(context);
offenderDAO= new OffenderDAO (context);
}
List<Infractions> getInfractions(int id) {
offenderDao.getOffender(id);
}
}
DAO 2
public class OffenderDAO {
private SQLiteDatabase database;
private SqLiteManager dbHelper;
private InfractionDAO infractionDAO;
public OffenderDAO (Context context) {
dbHelper = new SqLiteManager(context);
infractionDAO = new InfractionDAO(context);
}
Offender getOffender(int id) {
infractionDAO.getInfractions(id);
}
}
What I would do, is the following:
Solution 1.
create a base class(maybe make abstract?) for both, where they share the common things and in each of the separate classes, offernder and infraction put the different stuff.
Solution 2.
I would create a singleton class that in the constructor initializes both.
like in the following linke:
http://pastebin.com/84g9SgFT

Categories

Resources