Android testing - start with clean database for every test - sqlite

I'm testing my application with Android Instrumentation tests.
So I have a test-class extending ActivityInstrumentationTestCase2 which contains multiple tests. The code looks like this:
public class ManageProjectsActivityTestextends ActivityInstrumentationTestCase2<ManageProjectsActivity> {
public ManageProjectsActivityTest() {
super("eu.vranckaert.worktime", ManageProjectsActivity.class);
}
#Override
protected void setUp() throws Exception {
getInstrumentation().getTargetContext().deleteDatabase(DaoConstants.DATABASE);
super.setUp();
solo = new Solo(getInstrumentation(), getActivity());
}
#Override
protected void runTest() throws Throwable {
super.runTest();
getActivity().finish();
}
public void testDefaults() {
// My test stuff
}
public void testAddProject() {
// My test stuff
}
}
So the activity which is under test has a list of projects.
The list of projects is retrieved from the database. And when no database is available, so when the DB is created, I insert one default project.
So that means when the tests are run this is what I exepct:
The database, if available, is removed on the device
The first test is started (and thus the activity is launched which creates my DB with one project)
The tests uses the newly created DB, so meaning with only one project, during the test a second project is created
The first test is finished and the setUp() method is called again
The database, that should exist now, is again removed
The second test is started (and thus the activity is launched which creates my DB with one project)
The second test also finishes
But that's not quite what this test-suite does...
This is the result of my test-suite:
The database, if available, is removed on the device
The first test is started (and thus the activity is launched which creates my DB with one project)
The tests uses the newly created DB, so meaning with only one project, during the test a second project is created
The first test is finished and the setUp() method is called again
The database, that should exist now, is again removed
And here it comes: The second test is started (but my DB is not created again!!! I cannot see a file on the device either...) and the test should display only one project at the beginning but it does display already two!!!
The second test also finishes but fails because I have two projects at the beginning...
At the beginning I did not override the runTest() method but I thought that maybe I should end the activity myself to force the re-creation, but it doesn't make any difference.
So it seems that the DB is kept in memory (as even no new DB file is created on the device when I explicitly remove it). Or even the activity, because when putting a breakpoint a in the onCreate of the activity I only get in there once for both tests.
For the maintaining the DB I use ORMLite. You can see my helper class here: http://code.google.com/p/worktime/source/browse/trunk/android-app/src/eu/vranckaert/worktime/dao/utils/DatabaseHelper.java
So my question is how to force the tests to use a different DB all the time...?

A bit tangential to this problem, but I landed here when I was looking for help. Might be helpful to some folks. If you initialize your database with the RenamingDelegatingContext, it cleans the database between runs.
public class DataManagerTest extends InstrumentationTestCase {
private DataManager subject;
#Before
public void setUp() {
super.setUp();
RenamingDelegatingContext newContext = new RenamingDelegatingContext(getInstrumentation().getContext(), "test_");
subject = new DataManager(newContext);
}
// tests...
}
And the associated DataManagerClass.
public class DataManager {
private SQLiteDatabase mDatabase;
private SQLiteOpenHelper mHelper;
private final String mDatabaseName = "table";
private final int mDatabaseVersion = 1;
protected DataManager(Context context) {
this.mContext = context;
createHelper();
}
private void createHelper() {
mHelper = new SQLiteOpenHelper(mContext, mDatabaseName, null, mDatabaseVersion) {
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// createTable...
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
// upgrade table
}
};
}
...
}

mDb.delete(DATABASE_TABLE_NAME, null, null);
That is indeed the solution/way-to-go...
I changed the first line in my setUp(..) method to this:
cleanUpDatabase(tableList);
And then I added the method cleanUpDatabse(..) liek this:
private void cleanUpDatabase(List<String> dbTables) {
Log.i(LOG_TAG, "Preparing to clean up database...");
DatabaseHelper dbHelper = new DatabaseHelper(getInstrumentation().getTargetContext());
ConnectionSource cs = dbHelper.getConnectionSource();
SQLiteDatabase db = dbHelper.getWritableDatabase();
Log.i(LOG_TAG, "Dropping all tables");
for (String table : dbTables) {
db.execSQL("DROP TABLE IF EXISTS " + table);
}
Log.i(LOG_TAG, "Executing the onCreate(..)");
dbHelper.onCreate(db, cs);
Log.i(LOG_TAG, "Verifying the data...");
for (String table : dbTables) {
Cursor c = db.query(table, new String[]{"id"}, null, null, null, null, null);
int count = c.getCount();
if (count != 1 && (table.equals("project") || table.equals("task"))) {
dbHelper.close();
Log.e(LOG_TAG, "We should have 1 record for table " + table + " after cleanup but we found " + count + " record(s)");
throw new RuntimeException("Error during cleanup of DB, exactly one record should be present for table " + table + " but we found " + count + " record(s)");
} else if (count != 0 && !(table.equals("project") || table.equals("task"))) {
dbHelper.close();
Log.e(LOG_TAG, "We should have 0 records for table " + table + " after cleanup but we found " + count + " record(s)");
throw new RuntimeException("Error during cleanup of DB, no records should be present for table " + table + " but we found " + count + " record(s)");
}
}
Log.i(LOG_TAG, "The database has been cleaned!");
dbHelper.close();
}
This piece of code gets executed before every test, which makes all my tests independent from each other.
Caution: In order to retrieve a reference to your DatabaseHelper (your own implementation off course ;) ) you cannot call getActivity() because that will launch your activity (and thus do all your initial DB loading (if any..)

InstrumentationRegistry → getContext → deleteDatabase
android.support.test.InstrumentationRegistry's getTargetContext and, counterintuitively perhaps, getContext should do the trick:
Synopsis
Use getContext for deleting database
(not getTargetContext).
getContext().deleteDatabase(DbHelper.DATABASE_NAME);
Example
public class DbHelperTest {
private DbHelper mDb;
#Before
public void setUp() throws Exception {
getContext().deleteDatabase(DbHelper.DATABASE_NAME);
mDb = new DbHelper(getTargetContext());
}
#After
public void tearDown() throws Exception {
mDb.close();
}
#Test
public void onCreate() throws Exception {
mDb.onCreate(mDb.getWritableDatabase());
}
#Test
public void onUpgrade() throws Exception {
mDb.onUpgrade(mDb.getWritableDatabase(), 1, 2);
}
#Test
public void dropTable() throws Exception {
String tableName = "mesa";
mDb.getReadableDatabase().execSQL("CREATE TABLE "
+ tableName + "(_id INTEGER PRIMARY KEY AUTOINCREMENT)");
mDb.dropTable(mDb.getWritableDatabase(), tableName);
}
}

you try to start time all table data delete alter native solution.
mDb.delete(DATABASE_TABLE_NAME, null, null);

Since my db is encrypted I needed to remove the actual db file after each test case. I had trouble making deleteDatabase from a RenamingDelegatingContext since the RenamingDelegatingContext could not find the previously created db for some reason.
Solution for AndroidTestCase subclass:
#Override
protected void setUp() throws Exception {
super.setUp();
mContext = new RenamingDelegatingContext(getContext(), TEST_DB_PREFIX);
// create db here
}
#Override
protected void tearDown() throws Exception {
// Making RenamingDelegatingContext find the test database
((RenamingDelegatingContext) mContext).makeExistingFilesAndDbsAccessible();
mContext.deleteDatabase(DB_NAME);
super.tearDown();
}

before each test run it
val context: Context = ApplicationProvider.getApplicationContext()
context.deleteDatabase("myDatabaseName.db")
this can also be made a rule

Related

Multiple chained API calls to fetch data, but doOnNext of PublishSubject is never reached

I have a problem to understand a chained "RXJava-Retrofit" API call. I got inspired by this and implement this class named ObservationLoader to load the data from the API bucket per bucket. When the end of data is reached the API sends a endOfRecords=true:
public Observable<PageObject<Observation>> getAllObservationDataByRegion(long taxonKey,
String regionId) {
final PublishSubject<PageObject<Observation>> subject = PublishSubject.create();
return subject.doOnSubscribe(disposable -> {
this.getData(taxonKey, regionId, 0).subscribe(subject);
})
.doOnNext(observationPageObject -> {
if (observationPageObject.isEndOfRecords()) {
// -> list is completely loaded
subject.onComplete();
} else {
int nextOffset = observationPageObject.getOffset() + 1;
this.getData(taxonKey, regionId, null, nextOffset).subscribe(subject);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<PageObject<Observation>> getData(long id,
String regionId,
int offset) {
// Get your API response value
return this.api.getObservations(id, regionId, ObservationLoader.PAGE_LIMIT, offset);
}
In my Android fragment HomeFragment I subscribe to the ObservationLoader:
ObservationLoader loader = new ObservationLoader(this.getApi());
Observable<PageObject<Observation>> observable = loader
.getAllObservationDataByRegion(this.getSelectedSpecies(), this.getSelectedRegion());
observable.subscribe(new Observer<PageObject<Observation>>() {
#Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "ON_SUBSCRIBE");
}
#Override
public void onNext(PageObject<Observation> observationPageObject) {
Log.i(TAG, "ON_NEXT");
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "ERROR = " + e.getMessage());
}
#Override
public void onComplete() {
Log.i(TAG, "COMPLETED");
}
});
I can see that the onSubscribe() and doOnSubscribe() are called and even the getData() is reached. I assume the API is responding correctly (a previous attempt attempt with recursion worked fine). But I never reached the doOnNext function. The observer goes straight to onComplete() and no data is received. What could be the reason?
When doOnSubscribe runs, the doesn't see any consumers yet so if getData is synchronous, there won't be any first results to trigger further results. Also if getData ends, it will complete the setup so the next getData call in doOnNext will push to an already terminated subject, ingoring all data.
You'll need a differently organized feedback loop:
// we loop back the nextOffset, in a thread-safe manner
Subject<Integer> subject = PublishSubject.<Integer>create()
.toSerialized();
// bootstrap with 0 and keep open for more offsets
subject.mergeWith(Observable.just(0))
// get the data for the current offset
.concatMap(nextOffset -> getData(taxonKey, regionId, nextOffset)
.subscribeOn(Schedulers.io())
)
// if the response is end of records, stop
.takeWhile(observationPageObject -> !observationPageObject.isEndOfRecords())
// otherwise not end of records, feedback the new offset
.doOnNext(observationPageObject ->
subject.onNext(observationPageObject.getOffset() + 1)
)
// get the data on the main thread
.observeOn(AndroidSchedulers.mainThread());

toCompletableFuture() stucks for asynchronous cache

Hello there I am trying to force a promise to end to get the result from it but it just stucks on loading.
public class CacheController extends Controller {
private AsyncCacheApi cache;
public Result cache()
{
String test = "nice";
cache.set("item.key", test, 15);
Customer user = new Customer("Ana", 12);
CompletionStage<Done> result = cache.set(user.getName(), user);
block(result);
return ok("Cached");
}
public Result checkCache() throws Exception
{
Logger.info("start");
//CompletionStage<String> news = cache.get("item.key");
//news.thenRun(() -> System.out.println("works"));
CompletionStage<Customer> result = cache.get("Ana");
Logger.info("step 1");
Logger.info(cache.get("Ana").toString());
Logger.info("Step 2");
Customer c = block(result);
Logger.info("Step 3 " + c.getName());
//result.thenRun(() -> setUser(result)).thenRun(() -> Logger.info(user.getName() + " " + user.getAge()));
return ok("cancan");
}
private <T> T block(CompletionStage<T> stage) {
try {
return stage.toCompletableFuture().get();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
When trying to load the page it gets stuck after step2 at line 56: Customer c = block(result); by my guesses
Any ideas to fix it?
#Codrin
I had the same problem. But, see https://www.playframework.com/documentation/2.6.x/JavaCache#Setting-the-execution-context
By default, all Ehcache operations are blocking, and async implementations will block threads in the default execution context.
Maybe CompletableFuture.get() gets stuck because it is executed in the same thread with the caller.
Referring to the linked page, I added snippet below to my application.conf and it worked.
play.cache.dispatcher = "contexts.blockingCacheDispatcher"
contexts {
blockingCacheDispatcher {
fork-join-executor {
parallelism-factor = 3.0
}
}
}

Android Roboelectric 3.0 testing next activity –choosing from multiple activities

I have scenario like this:--
I have three activities ActivityA(launcher activity), ActivityB, ActivityC
Now in Activity A I read the Application context to decide whether to start ActivityB or ActivityC.
But even after setting the context value manual, the ActivityA is not updated and second test case fails. Any helps?
private ActivityA activityA;
private ShadowActivity shadowActivity;
#Before
public void setUp() {
activityA = Robolectric.setupActivity(ActivityA.class);
assertNotNull("ActivityA not intsantiated", activityA);
shadowActivity = Shadows.shadowOf(activityA);
}
#Test
public void shouldStartActivityB() throws Exception
{
Intent intent = shadowActivity.peekNextStartedActivity();;
assertEquals(ActivityB.class.getCanonicalName(), intent.getComponent().getClassName());
}
#Test
public void shouldStartMainActivity() throws Exception
{
ApplicationSettings.setWizardState(RuntimeEnvironment.application, 22);
ShadowLog.d("Wizard state value", "" +ApplicationSettings.getWizardState(RuntimeEnvironment.application));// it prints 22
activityA= Robolectric.setupActivity(ActivityA.class);
shadowActivity = Shadows.shadowOf(activityA);
Intent intent = shadowActivity.peekNextStartedActivity();
ShadowLog.d("target activity is", intent.getComponent().getClassName());// This prints ActivityB instead of ActivityC
assertEquals(ActivityC.class.getCanonicalName(), intent.getComponent().getClassName()); // this test case fails
}

AspectJ - Is is possible to extend an enum's value?

Say I have an enum
public enum E {A,B,C}
Is it possible to add another value, say D, by AspectJ?
After googling around, it seems that there used to be a way to hack the private static field $VALUES, then call the constructor(String, int) by reflection, but seems not working with 1.7 anymore.
Here are several links:
http://www.javaspecialists.eu/archive/Issue161.html (provided by #WimDeblauwe )
and this: http://www.jroller.com/VelkaVrana/entry/modify_enum_with_reflection
Actually, I recommend you to refactor the source code, maybe adding a collection of valid region IDs to each enumeration value. This should be straightforward enough for subsequent merging if you use Git and not some old-school SCM tool like SVN.
Maybe it would even make sense to use a dynamic data structure altogether instead of an enum if it is clear that in the future the list of commands is dynamic. But that should go into the upstream code base. I am sure the devs will accept a good patch or pull request if prepared cleanly.
Remember: Trying to avoid refactoring is usually a bad smell, a symptom of an illness, not a solution. I prefer solutions to symptomatic workarounds. Clean code rules and software craftsmanship attitude demand that.
Having said the above, now here is what you can do. It should work under JDK 7/8 and I found it on Jérôme Kehrli's blog (please be sure to add the bugfix mentioned in one of the comments below the article).
Enum extender utility:
package de.scrum_master.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
public class DynamicEnumExtender {
private static ReflectionFactory reflectionFactory =
ReflectionFactory.getReflectionFactory();
private static void setFailsafeFieldValue(Field field, Object target, Object value)
throws NoSuchFieldException, IllegalAccessException
{
// let's make the field accessible
field.setAccessible(true);
// next we change the modifier in the Field instance to
// not be final anymore, thus tricking reflection into
// letting us modify the static final field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(field);
// blank out the final bit in the modifiers int
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(field, modifiers);
FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
fa.set(target, value);
}
private static void blankField(Class<?> enumClass, String fieldName)
throws NoSuchFieldException, IllegalAccessException
{
for (Field field : Class.class.getDeclaredFields()) {
if (field.getName().contains(fieldName)) {
AccessibleObject.setAccessible(new Field[] { field }, true);
setFailsafeFieldValue(field, enumClass, null);
break;
}
}
}
private static void cleanEnumCache(Class<?> enumClass)
throws NoSuchFieldException, IllegalAccessException
{
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
blankField(enumClass, "enumConstants"); // IBM JDK
}
private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
throws NoSuchMethodException
{
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
parameterTypes[0] = String.class;
parameterTypes[1] = int.class;
System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
return reflectionFactory.newConstructorAccessor(enumClass .getDeclaredConstructor(parameterTypes));
}
private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues)
throws Exception
{
Object[] parms = new Object[additionalValues.length + 2];
parms[0] = value;
parms[1] = Integer.valueOf(ordinal);
System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
}
/**
* Add an enum instance to the enum class given as argument
*
* #param <T> the type of the enum (implicit)
* #param enumType the class of the enum to be modified
* #param enumName the name of the new enum instance to be added to the class
*/
#SuppressWarnings("unchecked")
public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName) {
// 0. Sanity checks
if (!Enum.class.isAssignableFrom(enumType))
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
// 1. Lookup "$VALUES" holder in enum class and get previous enum
// instances
Field valuesField = null;
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
if (field.getName().contains("$VALUES")) {
valuesField = field;
break;
}
}
AccessibleObject.setAccessible(new Field[] { valuesField }, true);
try {
// 2. Copy it
T[] previousValues = (T[]) valuesField.get(enumType);
List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
// 3. build new enum
T newValue = (T) makeEnum(
enumType, // The target enum class
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size(), new Class<?>[] {}, // could be used to pass values to the enum constuctor if needed
new Object[] {} // could be used to pass values to the enum constuctor if needed
);
// 4. add new value
values.add(newValue);
// 5. Set new values field
setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
// 6. Clean enum cache
cleanEnumCache(enumType);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
}
Sample application & enum:
package de.scrum_master.app;
/** In honour of "The Secret of Monkey Island"... ;-) */
public enum Command {
OPEN, CLOSE, PUSH, PULL, WALK_TO, PICK_UP, TALK_TO, GIVE, USE, LOOK_AT, TURN_ON, TURN_OFF
}
package de.scrum_master.app;
public class Server {
public void executeCommand(Command command) {
System.out.println("Executing command " + command);
}
}
package de.scrum_master.app;
public class Client {
private Server server;
public Client(Server server) {
this.server = server;
}
public void issueCommand(String command) {
server.executeCommand(
Command.valueOf(
command.toUpperCase().replace(' ', '_')
)
);
}
public static void main(String[] args) {
Client client = new Client(new Server());
client.issueCommand("use");
client.issueCommand("walk to");
client.issueCommand("undress");
client.issueCommand("sleep");
}
}
Console output with original enum:
Executing command USE
Executing command WALK_TO
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant de.scrum_master.app.Command.UNDRESS
at java.lang.Enum.valueOf(Enum.java:236)
at de.scrum_master.app.Command.valueOf(Command.java:1)
at de.scrum_master.app.Client.issueCommand(Client.java:12)
at de.scrum_master.app.Client.main(Client.java:22)
Now you can either add an aspect with an advice executed after the enum class was loaded or just call this manually in your application before extended enum values are to be used for the first time. Here I am showing how it can be done in an aspect.
Enum extender aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Command;
import de.scrum_master.util.DynamicEnumExtender;
public aspect CommandExtender {
after() : staticinitialization(Command) {
System.out.println(thisJoinPoint);
DynamicEnumExtender.addEnum(Command.class, "UNDRESS");
DynamicEnumExtender.addEnum(Command.class, "SLEEP");
DynamicEnumExtender.addEnum(Command.class, "WAKE_UP");
DynamicEnumExtender.addEnum(Command.class, "DRESS");
}
}
Console output with extended enum:
staticinitialization(de.scrum_master.app.Command.<clinit>)
Executing command USE
Executing command WALK_TO
Executing command UNDRESS
Executing command SLEEP
Et voilà! ;-)

Java collection retrieve object in reverse insertion order

I want to retrive object in reverse insertion order.
For example i have collection object where i have inserted following object
mango
apple
orange
while retriving it should come in reverse insert order i.e orange,apple,mango and this collection class should now allow duplicate object also. Is there any inbuilt API
is there in JDK 1.6 to do this?otherwise please tell me the logic to implement to do this.
Go for java.util.Stack which uses First in Last out policy. See docs for Stack
But read this too
Here is a example for you. I hope this will help you.
public class ReverseCollection {
ArrayList<String> al = new ArrayList<String>();
//add elements to the ArrayList
public static void main(String args[]){
ReverseCollection rc = new ReverseCollection();
rc.createList();
System.out.println(" ------ simple order ---------");
rc.print();
Collections.reverse(rc.getAl());
System.out.println(" ------ reverse order -------- ");
rc.print();
}
private void print() {
// TODO Auto-generated method stub
for (int i = 0; i < al.size(); i++) {
System.out.println(al.get(i));
}
}
public ArrayList<String> getAl() {
return al;
}
public void setAl(ArrayList<String> al) {
this.al = al;
}
private void createList() {
// TODO Auto-generated method stub
al.add("JAVA");
al.add("C++");
al.add("PERL");
al.add("PHP");
}
}
here I have used a inbuilt method of collectionc that is reverse
Note that this method will change in original variable value.

Resources