anyone could help me with the problem: how to test android apps which uses ormlite?
I'd like to know how to create mocks form dao. This code gets me a dao
public Dao<Account, Integer> getAccountDao() throws SQLException {
if (accountDao == null) {
accountDao = getDao(Account.class);
}
return accountDao;
}
My testing activity looks this:
public class OrmActivity extends OrmLiteBaseActivity<DatabaseHelper> {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initUi();
initUiListeners();
RuntimeExceptionDao<Account, Integer> accountDao = getHelper().getAccountDataDao();
Account account = new Account();
account.setName("name");
account.setPassword("password");
accountDao.create(account);
}
I dont want to create separate database for tests. Instend of it I want to use mocks.
Best regards
Just in case anyone else stumbles across this post. The issue I had was that you need an instance of an Activity so you can pass the context to the OpenHelperManager.getHelper(android.content.Context context, Class openHelperClass) method.
To get around this I create a ActivityInstrumentationTestCase2 test class to provide the context. You could use MockContext but I understand it to be problematic. This way is simple stupid, sort of :)
At that point I don't bother testing any of the Activity functionality, just database functionality.
public class TestActivity extends ActivityInstrumentationTestCase2<MainFragment> {
public TestActivity() {
super(MainFragment.class);
}
protected void setUp() throws Exception {
super.setUp();
//DatabaseHelper.class is the extended OrmLiteSqliteOpenHelper class
OpenHelperManager.getHelper(getActivity(), DatabaseHelper.class);
}
protected void tearDown() throws Exception {
OpenHelperManager.releaseHelper();
super.tearDown();
}
public void testDetailDiscount() {
//Example code using an entity class that encapsulates ormlite functionality
double total = 95 * 5;
DocumentDetail detail = DocumentDetail.create(getActivity());
assertEquals(total, detail.getTotal());
}
}
Hope it helps
I use Robolectric for unit testing Android apps. Works fine with RoboGuice and Ormlite. Robolectric creates its own database, so a unit test doesn't modify the database on the device you are testing with.
If this doesn't answer your question, please clarify your question. Like, how is not using a separate database related to using mock dao's?
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 need to determine in runtime from code if the application is run under TestInstrumentation.
I could initialize the test environment with some env/system variable, but Eclipse ADK launch configuration would not allow me to do that.
Default Android system properties and environment do not to have any data about it. Moreover, they are identically same, whether the application is started regularly or under test.
This one could be a solution: Is it possible to find out if an Android application runs as part of an instrumentation test but since I do not test activities, all proposed methods there won't work. The ActivityManager.isRunningInTestHarness() method uses this under the hood:
SystemProperties.getBoolean("ro.test_harness")
which always returns false in my case. (To work with the hidden android.os.SystemProperties class I use reflection).
What else can I do to try to determine from inside the application if it's under test?
I have found one hacky solution: out of the application one can try to load a class from the testing package. The appication classloader surprisingly can load classes by name from the testing project if it was run under test. In other case the class is not found.
private static boolean isTestMode() {
boolean result;
try {
application.getClassLoader().loadClass("foo.bar.test.SomeTest");
// alternatively (see the comment below):
// Class.forName("foo.bar.test.SomeTest");
result = true;
} catch (final Exception e) {
result = false;
}
return result;
}
I admit this is not elegant but it works. Will be grateful for the proper solution.
The isTestMode() solution did not work for me on Android Studio 1.2.1.1. Almighty Krzysztof from our company tweaked your method by using:
Class.forName("foo.bar.test.SomeTest");
instead of getClassLoader(). Thanks for Krzysztof!
We created a solution to pass parameters to the MainActivity and use it inside the onCreate method, enabling you to define how the Activity will be created.
In MainActivity class, we created some constants, which could also be an enum. We created a static attribute too.
public class MainActivity {
public static final int APPLICATION_MODE = 5;
public static final int UNIT_TEST_MODE = 10;
public static final int OTHER_MODE = 15;
public static int activityMode = APPLICATION_MODE;
(...)
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
switch (activityMode) {
case OTHER_MODE:
(...)
break;
case UNIT_TEST_MODE:
Log.d(TAG, "Is in Test Mode!");
break;
case APPLICATION_MODE:
(...)
break;
}
(...)
}
(...)
}
We made MainActivityTest class abstract, created a setApplicationMode and called this method inside the setUp() method, before calling the super.setUp() method.
public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
protected void setUp() throws Exception {
setApplicationMode(); // <=====
super.setUp();
getActivity();
(...)
}
(...)
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
}
}
All other test classes inherit from MainActivityTest, if we want it to have another behaviour, we can simply override the setApplicationMode method.
public class OtherMainActivityTest extends MainActivityTest {
(...)
#Override
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.OTHER_MODE;
}
}
The user nathan-almeida is the friend that is co-author of this solution.
As the title says. I am aware that there is a limited in memory database provided in robolectric. Is there any way to use this with Active Android? Under the default configuration, it appears that the database is cleared after all the tests are run, but not for each test.
I use greenDao - but the principle is the same.
My Application class initialises my DB (the DB has a name). For my tests I subclass Application (which allows Robolectric to call this version instead) and override the method that gets the DB name - and return null. This then means I create an in memory DB. As the Application creation is part of setUp then a new in memory DB is used for each test.
public class MyApplication extends android.app.Application {
#Override
public void onCreate() {
super.onCreate();
initialiseDB(getDatabaseName());
}
protected String getDatabaseName() {
return "regular-db-name";
}
private void initialiseDB(String dbName) {
// DB initialization
// one example would be:
Configuration.Builder builder = new Configuration.Builder(this);
builder.setDatabaseName(dbName);
ActiveAndroid.initialize(builder.create());
}
}
public class TestApplication extends MyApplication {
#Override
protected String getDatabaseName() {
// use fresh in memory db each time
return null;
}
}
I have the following test code. I am trying to test whether a list is being populated from SQLite database.
public class ViewIssuesActivityTest extends BaseActivityTest<ViewIssuesActivity>{
private List<Issue> issues;
public ViewIssuesActivityTest() {
super(ViewIssuesActivity.class);
}
public void setUp() throws Exception {
super.setUp();
issues = new ArrayList<Issue>();
issues.add(new Issue("Trial","Desc","Location","ImagePath"));
IssueRepository issueRepository = mock(IssueRepository.class);
doAnswer(new Answer<Object>() {
#Override
public List<Issue> answer(InvocationOnMock invocation) throws Throwable {
return issues;
}
}).when(issueRepository).getIssues();
activity.setIssueRepository(issueRepository);
}
public void testNumberOfIssuesRecorded() {
ListView listView = (ListView) activity.findViewById(android.R.id.list);
assertEquals(1, listView.getCount());
}
}
My BaseActivityTest code is:
public class BaseActivityTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
protected T activity;
public BaseActivityTest(Class<T> activityClass) {
super(activityClass);
}
#Override
protected void setUp() throws Exception {
activity = getActivity();
}
}
My ViewIssuesActivity is as follows:
public class ViewIssuesActivity extends ListActivity{
private IssueRepository issueRepository;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.change.kranti.R.layout.issues);
issueRepository = new IssueRepository(getApplicationContext());
List<Issue> issues = new ArrayList<Issue>();
issues = issueRepository.getIssues();
ArrayAdapter<Issue> adapter = new ArrayAdapter<Issue>(this,
android.R.layout.simple_list_item_1, issues);
setListAdapter(adapter);
}
}
The issue is I get error: expected<1> got <0>
I think the issue is the onCreate method is getting called before the issueRepository is created.
I want to mock the IssueRepository and test my list getting populated.
What is the issue with my code or is there a better way to test this functionality.
Any help will be appreciated.
You are right. The issue is that the onCreate method gets called before the mock issueRepository gets injected. When you call getActivity in the context of an ActivityInstrumentationTestCase2, all the usual life cycle methods get called (i.e., onCreate, onStart, and onResume). When your test code calls setIssueRepository, it's already too late, the issues have been fetched from the repository.
The best solution I know in this kind of case is to use dependency injection, whether by hand or using a DI framework.
It's not clear what you're trying to do.
You can't run two tests simultaneously against the same class. The ActivityInstrumentationTestCase2 class is essentially a controller and wrapper for the Activity under test, but it can only control a single Activity, and you can't control the same Activity with multiple instances of ActivityInstrumentationTestCase2.
To set up an app for test-driven development, try to use as many POJOs (Plain Old Java Objects) as you can. Limit the Activity testing to stuff such as sending Intents or integration between POJOs that you can mock.
ActivityInstrumentationTestCase2 is a JUnit TestCase subclass, but it has limitations.
That's probably part one of my question.
Basically I'm struggling with the actual injection for version 1.1.2. I've read the couple of pages on the site, and I feel I'm missing something.
Basically I've done the RoboApplication extension. I've overridden the addApplicationModules method. I've even made a module.
My module looks like this:
public class DataRepository extends AbstractAndroidModule
{
#Override
protected void configure() {
/*
* This tells Guice that whenever it sees a dependency on a TransactionLog,
* it should satisfy the dependency using a DatabaseTransactionLog.
*/
bind(IDataBaseAdapter.class).to(DataBaseAdapter.class);
}
}
In my adapter I have this:
public class DataBaseAdapter implements IDataBaseAdapter
{
private DataBaseHelper _dbHelper;
private SQLiteDatabase _db;
#Inject
protected static Provider<Context> contextProvider;
public DataBaseAdapter()
{
_dbHelper = new DataBaseHelper(contextProvider.get());
}
}
If I don't do there, where is the opportune place for the chunk of code to reside... where I associate injectors?
Finally... my Application has an injection of it like so:
public class MyApplication extends RoboApplication
{
public MyApplication()
{
super();
}
public MyApplication(Context context)
{
super();
attachBaseContext(context);
}
#Override
protected void addApplicationModules(List<Module> modules)
{
modules.add(new DataRepository());
}
#Inject
private IDataBaseAdapter adapter;
public IDataBaseAdapter getAdapter()
{
return adapter;
}
public void setAdapter(IDataBaseAdapter value)
{
adapter = value;
}
...
}
I'm trying to use the Inject attribute as shown. For example:
#Inject
private IDataProvider provider;
A couple of reasons why I'm lost is that I come from a .NET and Flash/ActionScript background plus I've only used StructureMap instead of Ninject (in the .NET world), which I've heard Guice is designed with some of the ideas of Ninject in mind. Could someone help me figure out this small piece?
I'd really like to focus on using 1.1.2 instead of jumping to 2.x of RoboGuice... especially since it is still in beta, so I hope you all don't mind.
Thanks again,
Kelly
Android is quite different from standalone / hosted java application. You do not have main() , but you have certain activity units, which are managed by android framework (activities, services , broadcast receivers)
DI is a technique which allows you to eliminate booler plate code by wiring together
parts in good object oriented way.
As your unit of work is mostly activity, you shall do wiring / creation of your collaborating objects in onCreate() method , and there are dedicated onResume() and onPause() methods (see actviity lifecycle)
Rule of thumb is, does this thing needs to be restarted every time activity loses it focus? If yes, initialize / destroy it in inResume() / onPause(), otherwise - in onCreate()
And if you like to share objects withing entire application ( running in same JVM ) , it is OK to use singleton pattern in android. So you may just have singleton injector factory , and cosult it from everywhere:
InjectorFactory.getInstance(<context if necessary?>).getInstance(whatever you need);
OK, I've figured out what was needed, but I'm not quite sure why after seeing all the information floating out there.
I basically made this change, and now my test passes.
public class DataBaseAdapter implements IDataBaseAdapter
{
private DataBaseHelper _dbHelper;
private SQLiteDatabase _db;
#Inject
public DataBaseAdapter(Provider<Context> contextProvider)
{
_dbHelper = new DataBaseHelper(contextProvider.get());
}
}
While I like using constructors as the tool for injecting, I wonder why it had to work this way, considering that examples I have seen are some kind of reflection class injection.
Anyway, that's this part. Hopefully someone else will find this useful.
Cheers,
Kelly