I want to unit test a DatabaseService class I have created ( Modified a WishList application I found online )
package com.test.db;
import java.sql.SQLException;
import java.util.List;
import android.content.Context;
import com.test.model.WishList;
public class WishListService
{
private static WishListService instance;
public static void init(Context ctx)
{
if (null == instance)
{
instance = new WishListService(ctx);
}
}
static public WishListService getInstance()
{
return instance;
}
private DatabaseHelper helper;
private WishListService(Context ctx)
{
helper = DatabaseHelper.getInstance(ctx);
}
public List<WishList> getAllWishLists()
{
List<WishList> wishLists = null;
try
{
wishLists = helper.getWishListDao().queryForAll();
} catch (SQLException e)
{
e.printStackTrace();
}
return wishLists;
}
public void addWishList(WishList l)
{
try
{
helper.getWishListDao().create(l);
} catch (SQLException e)
{
e.printStackTrace();
}
}
public WishList getWishListWithId(int wishListId)
{
WishList wishList = null;
try
{
wishList = helper.getWishListDao().queryForId(wishListId);
} catch (SQLException e)
{
e.printStackTrace();
}
return wishList;
}
public void deleteWishList(WishList wishList)
{
try
{
helper.getWishListDao().delete(wishList);
} catch (SQLException e)
{
e.printStackTrace();
}
}
public void updateWishList(WishList wishList)
{
try
{
helper.getWishListDao().update(wishList);
} catch (SQLException e)
{
e.printStackTrace();
}
}
}
My question is is there any way to instantiate the DatabaseHelper without having to create a test activity and pass it in as the Context that DatabaseHelper requires?
Ideally I want to Unit test this class as a standard JUnit test , not an Android JUnit test
I'm not sure this is a good idea. ORMLite has non-Android JDBC package that you could use to drive Sqlite directly via JDBC. However if the goal is to test your Android database classes, you will be running a lot of non-android code which would, in my view, invalidate the tests. I'm considered mocking out all of the Android classes but it starts to become a maze of twisting passages before long.
I think your best way to proceed would be to put up with the Android junit tests. Take a look at the ORMLite Android test package files for examples. The [BaseDaoTest file] has all of the setup/shutdown methods that you can customize for your own tests.
Related
I'm trying to setup roboelectric and fest in my own project. However when I try to run ./gradlew clean test in the command line I get the following errors in the test report:
http://pastebin.com/5gaJgftf
My project does build the app without errors though. I only get this issue when I try to run tests, so it seems that Roboelectric is not aware is not aware of my native sqlcipher binaries and other binaries.
So I tried loading it with a shadow class for the runner that loads up the necessary binaries:
#Config(emulateSdk = 18, shadows={MyJniClass.class})
#RunWith(RobolectricTestRunner.class)
public class MainActivityBuildTest {
#Test
public void testSomething() throws Exception {
Activity activity = Robolectric.buildActivity(MainActivity.class).create().get();
assertTrue(activity != null);
}
}
Using my custom jniloader shadow class
#Implements(RobolectricTestRunner.class)
class MyJniClass {
static {
try {
System.loadLibrary("libdatabase_sqlcipher");
System.loadLibrary("libdatabase_android");
System.loadLibrary("libstlport_shared");
} catch (UnsatisfiedLinkError e) {
// only ignore exception in non-android env
if ("Dalvik".equals(System.getProperty("java.vm.name"))) throw e;
}
}
}
You have issues to use sql cipher with robolectric?
My workaround is to use two different implementation of the SQLiteOpenHelper. One use sqlcipher and the another one the default database implementation. This both are behind a factory class, which create the SQLiteDatabase based on a static boolean flag, so the unscure database handling will be eliminated from progard.
The next issue is that both have different SQLiteDatabase classes. So again build a wrapper around the SQLiteDatabase which will be created with the right SQLiteDatabase from the SQLiteOpenHelper Wrapper. Take the Cipher variant as your base. you can ignore methods which exist at default SQLiteDatabase but not at the cipher variant. This wrapper class take the same static boolean flag to choose which database should be used. if make a mistake and take the wrong database then it should throw a null pointer exception ;)
in your app code you should now use only the wrapper classes.
example for DatabaseHelper wrapper
public class MyDatabaseHelper {
public static final String DATABASE_NAME = "my.db";
public static final int DATABASE_VERSION = 1;
MyEncryptedDatabaseHelper encryptedDatabase;
MyUnsecureDatabaseHelper unsecureDatabase;
public MyDatabaseHelper(Context context) {
if (ReleaseControl.USE_UNSECURE_DATABASE) {
unsecureDatabase = new MyUnsecureDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
return;
}
encryptedDatabase = new MyEncryptedDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public MySQLiteDatabase getWritableDatabase(String password) throws MySQLiteException {
if (ReleaseControl.USE_UNSECURE_DATABASE) {
try {
return new MySQLiteDatabase(unsecureDatabase.getWritableDatabase());
} catch (android.database.SQLException e) {
throw new MySQLiteException(e);
}
}
try {
return new MySQLiteDatabase(encryptedDatabase.getWritableDatabase(password));
} catch (net.sqlcipher.database.SQLiteException e) {
throw new MySQLiteException(e);
}
}
}
and short snippet from SQLiteDatabase wrapper
public class MySQLiteDatabase {
private net.sqlcipher.database.SQLiteDatabase encryptedDatabase;
private android.database.sqlite.SQLiteDatabase unsecureDatabase;
public MySQLiteDatabase(SQLiteDatabase database) {
encryptedDatabase = database;
}
public MySQLiteDatabase(android.database.sqlite.SQLiteDatabase database) {
unsecureDatabase = database;
}
public static void loadLibs(android.content.Context context) {
if (ReleaseControl.USE_UNSECURE_DATABASE) { return; }
SQLiteDatabase.loadLibs(context);
}
public static int releaseMemory() {
if (ReleaseControl.USE_UNSECURE_DATABASE) {
return android.database.sqlite.SQLiteDatabase.releaseMemory();
}
return net.sqlcipher.database.SQLiteDatabase.releaseMemory();
}
public static SQLiteDatabase openDatabase(String path, String password, MyCursorFactory factory, int flags) {
if(factory == null) factory = new NullCursorFactory();
if (ReleaseControl.USE_UNSECURE_DATABASE) {
return new MySQLiteDatabase(android.database.sqlite.SQLiteDatabase.openDatabase(path, factory.getUnsecure(), flags));
}
return new MySQLiteDatabase(net.sqlcipher.database.SQLiteDatabase.openDatabase(path, password, factory.getEncrypted(), flags));
}
In robolectric test i set the USE_UNSECURE_DATABASE per reflection true
Do custom persisters work on Android? I was trying to write one for an entity, and was having no luck in having it run when the entity gets written by the DAO. So, I tried to use the "MyDatePersister" from the examples and I am not able to get that working either.
The persister is nearly identical to the example one -> https://github.com/j256/ormlite-jdbc/blob/master/src/test/java/com/j256/ormlite/examples/datapersister/MyDatePersister.java
In my entity, I have
#DatabaseTable
public class ClickCount implements Serializable {
// other declarations
#DatabaseField(columnName = DATE_FIELD_NAME, persisterClass = MyDatePersister.class)
private Date lastClickDate;
// more code
}
Here is a link to the whole project in Bitbucket -> https://bitbucket.org/adstro/android-sandbox. It's basically one of the ORMLite Android examples with the custom persister example added.
Any feedback would be greatly appreciated.
Thanks
First off, what is the error result you're getting?
I got my custom persister to work just fine, though I didn't try to extend the DateType. Below is a JSONArrayPersister I found the need for. The confusing part is in the naming of the methods, but once they're setup properly, it should be ok.
package com.example.acme.persister;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.field.types.BaseDataType;
import com.j256.ormlite.support.DatabaseResults;
import org.json.JSONArray;
import org.json.JSONException;
import java.sql.SQLException;
public class JSONArrayPersister extends BaseDataType {
public static int DEFAULT_WIDTH = 1024;
private static final JSONArrayPersister singleTon = new JSONArrayPersister();
public static JSONArrayPersister getSingleton() {
return singleTon;
}
private JSONArrayPersister() {
super(SqlType.STRING, new Class<?>[] { String.class });
}
protected JSONArrayPersister(SqlType sqlType, Class<?>[] classes) {
super(sqlType, classes);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
try {
return new JSONArray(defaultStr);
} catch (JSONException ex)
{
return new JSONArray();
}
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
try {
return new JSONArray( results.getString(columnPos) );
} catch (JSONException ex)
{
return new JSONArray();
}
}
#Override
public Object resultStringToJava(FieldType fieldType, String stringValue, int columnPos) throws SQLException {
return parseDefaultString(fieldType, stringValue);
}
#Override
public int getDefaultWidth() {
return DEFAULT_WIDTH;
}
}
Then in your entity:
#DatabaseField(persisterClass = JSONArrayPersister.class)
private JSONArray something;
I am a beginner in android programming.
I want to use the hidden method "getState()" of "com.android.internal.telephony.call" package to manage the state of an outgoing call such as activating, ringing, answering, rejecting and disconnecting.
But there is an error in the following code on the line indicated by "**".
Any help?
My code is :
import com.android.internal.telephony.*;
public class MainActivity extends Activity {
Class myclass;
ClassLoader cloader;
Method f;
Object o;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cloader = this.getClass().getClassLoader();
try {
myclass = cloader.loadClass("com.android.internal.telephony.Call");
// No error generated. "Call" class will be loaded.
}
catch (Exception e)
{
System.out.println(e.toString());
}
try {
f = myclass.getMethod("getState", null);
// No error generated.Method "f" will be assigned
} catch (Exception e) {
System.out.println(e.toString());
}
Constructor constructors[] = myclass.getDeclaredConstructors();
// There is one constructor only
Constructor constructor = null;
for (int i=0; i<constructors.length;i++)
{
constructor = constructors[i];
if (constructor.getGenericParameterTypes().length == 0)
break;
}
constructor.setAccessible(true);
try {
o = constructor.newInstance(null);
//*****an exception generated here.
//*****Exception is "java.lang.instantationexception"
}
catch (Exception e) {
System.out.println(e.toString());
}
try {
f = myclass.getMethod("getState", null);
// No error
} catch (Exception e) {
System.out.println(e.toString());
}
}
Don't try to call private members like this. It will not work across Android versions and even across manufacturer customized ROMs of the same version.
I am using ormLite to store data on device.
I can not understand why but when I store about 100 objects some of them stores too long time, up to second.
Here is the code
from DatabaseManager:
public class DatabaseManager
public void addSomeObject(SomeObject object) {
try {
getHelper().getSomeObjectDao().create(object);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public class DatabaseHelper extends OrmLiteSqliteOpenHelper
public Dao<SomeObject, Integer> getSomeObjectDao() {
if (null == someObjectDao) {
try {
someObjectDao = getDao(SomeObject.class);
} catch (Exception e) {
e.printStackTrace();
}
}
return someObjectDao;
}
Any ideas to avoid this situations?
Thanks to Gray!
Solution is, as mentioned Gray, using callBatchTasks method:
public void updateListOfObjects (final List <Object> list) {
try {
getHelper().getObjectDao().callBatchTasks(new Callable<Object> (){
#Override
public Object call() throws Exception {
for (Object obj : list){
getHelper().getObjectDao().createOrUpdate(obj);
}
return null;
}
});
} catch (Exception e) {
Log.d(TAG, "updateListOfObjects. Exception " + e.toString());
}
}
Using this way, my objects (two types of objects, 1st type - about 100 items, 2nd type - about 150 items) store in 1.7 sec.
See the ORMLite documentation.
I want to call CRUD operations on Order objects in my Activity. I was wondered is the following implementation of a "Service" class a good way to do this? I don't want any reference to DatabaseHelper or DAO objects in my Activity code as I don't think this would be desireable.
Here is my Service class
public class OrderService
{
private static OrderService instance;
private static Dao<Order, Integer> orderDAO;
static public void init(Context ctx) {
if (null == instance) {
instance = new OrderService(ctx);
}
}
public static OrderService getInstance() {
return instance;
}
private OrderService(Context ctx) {
DatabaseHelper helper = DatabaseHelper.getInstance(ctx);
helper.getWritableDatabase();
orderDAO = helper.getOrderDao();
}
public Order getOrderWithId(int orderId) {
Order myOrder = null;
try {
myOrder = orderDAO.queryForId(orderId);
} catch (SQLException e) {
e.printStackTrace();
}
return myOrder;
}
public Order neworder(Order order) {
try {
orderDAO.create(order);
} catch (SQLException e) {
e.printStackTrace();
}
return order;
}
public void deleteorder(Order order) {
try {
orderDAO.delete(order);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void updateorder(Order order) {
try {
orderDAO.update(order);
} catch (SQLException e) {
e.printStackTrace();
}
}
public List <Order> getordersForCategory(int orderId) {
List <Order> orders = null;
try {
orders = orderDAO.queryForAll();
} catch (SQLException e) {
e.printStackTrace();
}
return orders;
}
}
and here is how I intend to use the service
public class OrderProcessingActivity extends Activity {
int orderID;
private Order order;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myview);
order = OrderService.getInstance().getOrderWithId(orderID);
......
Does this look like a good way to access the SQLlite DB ?
I have read about "Service" implementations that can be configured in Android so I was sondered is this something that I should be using instead?
Despite moving your database logic to a different class, you're doing all of your database operations in the UI thread, which is not ideal. Also note that even though your class is called "service" it doesn't inherit from any of the Service classes in Android.
One alternate approach would be to do your database operations from the doInBackground method of an AsyncTask, return your needed data from that method. Then, use the returned data to update your activity in the onPostExecute method.
This is more or less the approach I take. My application architecture typically looks like this:
Activity <--> Service <--> DAO <--> SQLite
This looks pretty close to what you have, so I'd say it looks good! I normally don't implement it as a singleton, however, as I don't like to keep the same Context around for the entire lifetime of the app. Instead, I pass in the Context to create a service from each Activity.