I'm newbie writing unit tests for my Android app. Currently I'm testing ContentProviders (like here) and I recently finished one and all fine doing CRUD operations tests for a "satellite" SQLite table ("satellite"=no foreign keys). Now I have to test another ContentProvider that perform operations in other table that have a foreign key to "satellite" table AND filesystem (images) files.
My issues are (questions reference to the ContentProvider being tested):
How to test ContentProvider if I need data from "satellite" table and (images) files? I need to create (in setUp() method) whole scenario (satellite data and images files) before running tests? And how? Think about this scenario: select * from table join satellite where satellite.id = ? (query return a path to image column too). I mean, tables and file system have to have some data to ensure query return rows and test if file exists, right?
ContentProvider is designed as an endpoint to trade data for SQlite table and images files in this way: insert() method receive temporal image file path and user data associated to the image. This ContentProvider move temporal image file to it's own logic inside custom path; and insert user data and previous path to SQlite table. Next query() method return image path and user data as a row. While inserting, I have to call an utility static method that return custom path, for instance this method: Uri origPath( Context context, long idC, long idP, String fileName ) called using FileUtility.origPath(...) inside ContentProvider#insert() (Context param came from testing ContentProvider). When testing, I have a
java.lang.UnsupportedOperationException
at android.test.mock.MockContext.getExternalFilesDir(MockContext.java:186)
I know that external/system calls by default throw an exception like that, but how to test my ContentProvider if it do a call to a static method from utility class to work? (like this) I need to refactor my code to adapt it to test work? or I have to change my test code for something else?
Can I call a ContentProvider from other ContentProvider? or is a bad design?
Is more useful to create a FileProvider and deal with all user camera images with that provider?, By design it have to be called inside ContentProvider anyway (like 3.)
Feel free to explain how my design can change for sake of code lord, or if there is a pattern for that, or I missing something.
Related
I am creating (in android studio) an app which has a couple basic tables. One will be a bunch of exercises (pull ups, push ups ect) and I would like to put a ton of common ones into the table by default.
Where in my code would be the logical place to do that? I have made a databaseHelper class which extends SQLiteOpenHelper. Just not sure if I should..
insert them all in onCreate()
make a databaseHelper method which inserts them all and call it elsewhere
other?
Create a database with your desired data, package it as an asset, and use SQLiteAssetHelper to automatically unpack it into the proper spot for you when you first try to work with the database. This will be faster than running your own transaction(s) to insert the data.
I have a doubt... I want to execute this code, but I don't know if it's better to do it in an Asynctask or it isn't necessary and I can execute it in the main function.. What do you think?
The SQLite Database is local, so the acces is so fast.. Thank you!
/*Here I create an object of SQLiteHelper Class*/
SQLiteHelper bbDD;
bbDD = new SQLiteHelper(getActivity());
/* Calling this method I open the database connection */
bbDD.open();
/* This method returns me an ArrayList, doing a SELECT in the SQLite Local Database*/
arrayList = bbDD.selectAll();
/* Here I Close the database connection */
bbDD.close();
Today's devices are fast enough to handle SQLite query inside main UI thread, but when you try to query big size of data inside SQLite (for example if you have more than hundred rows of data inside that database (Especially if row contains base64 encoded image strings)) user can see some UI lag, that's why query in Asynctask makes UI faster - without lags. If you don't have big data inside SQLite, there is no need to use AsyncTask for query.
No dude there is no need to write SQLite local querys in an Asyntask.
The answer to your question depend on the size of your database.
I have experienced to select hundreds of rows, with lot of columns on SQLite. It does not make the apps very lag, but it does make the apps a little bit lag (and my client can feel this). So, i moved the select operation to the onBackground in the Async class, and the apps run flawlessly again.
If you only have a little data, theres no need to put it on the background.
In the onCreate() method of my main activity I call the constructor of my dbManager, I call the open function that creates an instance of a SQLiteOpenHelper and than I call the getWritableDatabase() on it.
Within the UIThread I add records to the database and save those records to an ArrayList. Two other threads check the ArrayList do stuff and than update the list and the database.
Now I want to add a button in the UI to delete both database and list records using an AsyncTask.
I've read that the SqliteOpenHelper object holds on one database connection. So if there is one helper instance there is only one db connection. This connection could be used from multiple threads and the SqliteDatabase object uses java locks to keep access serialize. So If I have multiple threads writing on the database, a thread will wait until the previous one has finished his operation?
Adding the new functionality (remove all) could create problems, because I have to avoid that one of the two threads may try to edit a record that no longer exists. How can I achieve my goal? Thanks.
The databese:
As long as you use only one helper, you are thread safe without a need to do anything.
In a multi threaded app, you'll need synchronized only on the creation of the helper.
You can use transacion if you want to define a critical section (more than one atomic operation).
Wrap each call with getWritableDatabase() and close(false):
public void doSomething() {
SQLiteDatabase tdb = getWritableDatabase();
dbInstance.writeSomething(tdb, 1);
close(false);
}
In this way I have multiple threads reading and writing to the database without any problem.
You app logic:
Use memory to keep track of object state, and use the database as storage only. So if one thread deletes rows from the database - you can immediately update your objects in memory, and handle your ui accordingly.
If your data is very large, than you can keep in memory only a SparseArray of dirty rows id.
It may be useful to synchronize some operations here.
So If I have multiple threads writing on the database, a thread will
wait until the previous one has finished his operation?
Doesn't need to be so. See this for more details. Anyway, this is up to the DB engine and should be transparent.
Adding the new functionality (remove all) could create problems,
because I have to avoid that one of the two threads may try to edit a
record that no longer exists. How can I achieve my goal?
Have only one thread update the DB. Other threads only modify the ArrayList -also watch out because ArrayList is not thread-safe, consider using Collections#synchronizedList().
I've done my homework; I know all the ways to query an SQLite db from an Activity.
The problem is that all the examples ASSUME that i want to "load" data from the db onto the FIRST Activity screen.
But when my app's FIRST Activity is loaded I DON'T WANT TO GET ANY DATA FROM THE DB; i just want to:
(1) Check if the db file has already been created (if the setup routines have already run).
(2) If the db exists, load the SECOND Activity (with ContentProvider/Loaders, etc.) so the user can start adding data.
OR
(2) If the db DOESN'T exist, WHILE STILL IN THE FIRST ACTIVITY run the setup routines (create the db/tables from an *.sql file & INSERT the dummy data where needed)...then load the SECOND Activity (with ContentProvider/ Loaders, etc.) so the user can start adding data.
To me, the simple operation of creating the db/tables shouldn't require all the OVERHEAD of a ContentProvider and a bunch of Cursors and Loaders.
Is there anybody who could point me to a SIMPLE solution? Thanks!
Yaqub's link was helpful...
...what i did was create public static final String arrays in a DBConstants class containing the commands to create the Database on first run.
I have an activity for initialising a game, that does multiple selects and inserts from a number of SQLite tables.
I'm trying to understand AsyncTask, but, from all the examples I've read so far, I'm wondering if I am going to have to subclass AsyncTask for every single different data operation I need to do?
For example, my NewGame Activity does the following:
1) Insert new player record into PLAYER table
2) Insert new player's pet record into PET table
3) Select cursor of n records from INVENTORY
4) Insert array of ranomly chosen inventory items into PLAYER_OWNED table
5) ....more things of a similar nature
There are going to be a few more selects and inserts for various things too, so having an individual subclass for each one is going to get crazy. Not to mention that there will be about 8 activities for this game, all relying heavily on database reads and writes.
So, basically, how do I best use AsyncTask to carry out a number of different SQLite operations?
You can pass parameters to a AsyncTask, even more, if you use nested clases, you can use global variables from inside the AsyncTask class, by using one of the above or both mentioned aids you should be able to use the same class and have it do diferent things depending on the parameter you pass. I see no real need to define multiple AsyncTasks.
You will need to define a AsyncTask in every activity.
I wrote need, because you really dont have to, but its comfortable to do it this way, and its easy to read/write code, as the AsyncTask is asociated to the activity only. This is of course suposing you use nested clases, I see no point in writing a separate class file just for an AsyncTask.