I have Activity that contains few Fragments, now I would like to test one of this Fragment but I would like to separate test and test only core functionality of selected Fragment not bothering what is happening in main Activity.
My idea is to create a mock Activity which will just add Fragment in onCreate() method. Then I will make some tests. But I would not like to include mock Activity to my main project, I would rather include it to test project. So I did something like this:
I have created MockActivity:
public final class ActivityMock extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentTransaction t = getFragmentManager().beginTransaction();
MyFragment f = new MyFragment();
t.add(f, "MY_FRAGMENT");
t.commit();
}
}
I want to test it like this:
public final class MyFragmentTest extends
ActivityInstrumentationTestCase2<ActivityMock> {
public MyFragmentTest() {
super(ActivityMock.class);
}
public void testSomething() {
ActivityMock mActivity = getActivity();
//do some assertions
}
}
The problem is that I I get error:
java.lang.RuntimeException: Unable to resolve activity for: Intent {
act=android.intent.action.MAIN flg=0x10000000
cmp=com.example/.test.ActivityMock }
Ok next I tried to modify test project AndroidManifest.xml
<activity android:name=".ActivityMock" />
But I got same error. I think that is because anyway during test run main project is searched for ActivityMock. So I tried to add
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.example.test" />
I don know if it is a good idea, but main thought is that test project will be able to test (instrument) itself. But now I get:
junit.framework.AssertionFailedError: Exception in constructor: testSomething
(java.lang.NoClassDefFoundError: com.example.test.ActivityMock
So I think that modified AndroidManifest.xml worked but still ActivityMock class is being searched in main project, though it is in test project.
I assume that getActivity() method always look for activity class in main project.
Does anybody tried to test Fragment this way and was able to create Activity mock?
Cheers
Related
I am working on an Android project where I have a library project which has an activity.
I have a main app project which references the library project and tries to start the activity within the library project. In the library project I have the following activity:
public class DirectoryPicker extends Activity
{
GridView gridView = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.directory_picker);
gridView = (GridView)findViewById(R.id.directory_picker_gridview);
DirectoryAdapter.directories.add("String1");
DirectoryAdapter.directories.add("String2");
DirectoryAdapter.directories.add("String3");
DirectoryAdapter.directories.add("String4");
gridView.setAdapter(new DirectoryAdapter());
}
}
I am trying to start the activity using the following in my main application project.
protected OnPreferenceClickListener mPrefDefaultBackupClickListener = new OnPreferenceClickListener()
{
#Override
public boolean onPreferenceClick(Preference preference)
{
Intent intent = new Intent(getActivity(), DirectoryPicker.class);
startActivity(intent);
return false;
}
};
In my main application project AndroidManifest file I've added the following:
<activity android:name="com.MyCompany.Library.DirectoryPicker">
</activity>
When I try and launch the activity I get the following exception:
java.lang.NoClassDefFoundError: com.MyCompany.Library.DirectoryPicker
I can't see any reason why it doesn't work.
I've managed to find out the problem finally. Didn't manage to find any documentation but just trial and error and found if I go to File > Project Structure > Depdencies and then select my library and change the scope from compile to Provided and clean and rebuild everything the activity is then launched correctly.
I have a number of custom Android components and wish to test them using Espresso. As an Espresso test runs against an Activity I added a simple Activity class to the androidTest directory which programatically creates a view with my component in it ready for testing.
For example if I'm testing a MyView component then my Espresso test class might look something like this:
public class MyViewTest extends ActivityInstrumentationTestCase2<MyViewTestActivity>
{
private MyViewTestActivity activity;
public MyViewTest()
{
super(MyViewTestActivity.class);
}
#Override
protected void setUp() throws Exception
{
super.setUp();
setActivityInitialTouchMode(false);
// Launches the activity
activity = getActivity();
}
// Ensure that expected items are present
public void testLayout()
{
onView(withId(activity.view.getId())).check(matches(isDisplayed()));
}
}
with a simple MyViewTestActivity as follows:
public class MyViewTestActivity extends Activity
{
private static final Random RANDOM = new Random();
public LinearLayout layout;
public MyView view;
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
layout = new LinearLayout(this);
layout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
view = new MyView(this);
view.setId(RANDOM.nextInt());
view.setItem("Test text");
layout.addView(view);
setContentView(layout);
}
}
My problem is I appear to need to add the test Activity class, in this case MyViewTestActivity, to the main AndroidManifest.xml to make this work, otherwise I receive an Unable to resolve activity for: Intent... error when attempting to run the test. However I now have test activities in the main manifest, which seems like a bad thing to do.
How can I set up test-specific activities which are included in the test manifest but not the main one?
I'm using the gradle-based build system for Android.
Yes, you have to add MyViewTestActivity to the main AndroidManifest.xml.
If you look at the ActivityInstrumentationTestCase2 source code, you will see that getActivity() looks for the tested Activity in the target (i.e., the app under test) context.
Here is the relevant part of the source code.
#Override
public T getActivity() {
// ...
final String targetPackage = getInstrumentation().getTargetContext().getPackageName();
// ...
a = launchActivity(targetPackage, mActivityClass, null);
// ...
setActivity(a);
// ...
}
What I do in my projects is that I create a generic TestingActivity, put it in a .test package in the target app, and use it for all GUI-component testing. It is not ideal, but I never had any problem with this approach.
For a Bluetooth library I want to call a startActivityForResult to enable Bluetooth. I want to do this in my own library project. The problem is that sartActivityForResult only works for Activities, my library is not an activity, of course my project using this library project is having an activity. I tried two things
1.
Had my library to extend Activity, so I am creating the object like a normal POJO. Hence when calling startActivityForResult it throws a null pointer.
2.
Tried to get my Activity to pass it into the constructor of my library object. I am not able to to get an activity object, only context but that doesn't help.
What can I do?
EDIT: Some Code
I have the following class in my library project:
public class mylib
{
public mylib()
{
// Do some bluetooth setup thing here
// figure out that bluetooth is not enabled so try to enable it now
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
onActivityResult()
{
// Report back that BT is enabled now
}
}
Then I have of course my Android application using this library project in onCreate() I do something like this
public class Myactivity() extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
// do all the normal stuff
mylib pojo = new mylib(); // This creates a POJO which needs to startActivityForResults(...), see above code
}
}
The two options I mentioned above
I don't see how I can convince mylib to call startActivityForResult doing
public class mylib extends Activity
doesn't seem to do the trick.
I could overload my constructor of mylib to pass in the activity object, I have no idea how to create the activity object.
Hope that makes it a bit clearer.
You can create a constructor of your java class which has parameter of type Activity like below,
public class MyClass
{
private Activity activity;
public MyClass ( Activity activity )
{
this.activity = activity;
}
}
Now you can use this activity variable to call startAcvitiyForResult from your Java class.
My Android App was built on Single Activity, multiple fragments based model.
I need to do unit testing for the app. I could write unit testcases for app which contains all activities using ActivityInstrumentationTestCase2 JUnit but not for app which contains fragments.
Please suggest the way to write JUnit testcases for fragments.
Thank you
See Android: Testing fragments
Copied for your reading pleasure with edits made for getFragmentManager() vs getSupportFragmentManager() and android:exported="false":
If you want to test a fragment in isolation, you need to create a Test FragmentActivity so your test can use that. The test activity will look something like this. Remember to declare it in your application’s manifest:
public class TestFragmentActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.activity_fortests);
}
}
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="#+id/activity_test_fragment_linearlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
/>
</RelativeLayout>
AndroidManifest:
...
<activity
android:name="your.package.name.TestFragmentActivity"
android:exported="false" />
...
Then in your test project, you can have a class like this to start the fragment:
public class FrameworkObjectsGeneratorFragmentTest
extends ActivityInstrumentationTestCase2<TestFragmentActivity> {
private TestFragmentActivity mActivity;
public FrameworkObjectsGeneratorFragmentTest() {
super(TestFragmentActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
}
private Fragment startFragment(Fragment fragment) {
FragmentTransaction transaction = mActivity.getFragmentManager().beginTransaction();
transaction.add(R.id.activity_test_fragment_linearlayout, fragment, "tag");
transaction.commit();
getInstrumentation().waitForIdleSync();
Fragment frag = mActivity.getFragmentManager().findFragmentByTag("tag");
return frag;
}
public void testFragment() {
FrameworkObjectsGeneratorFragment fragment = new FrameworkObjectsGeneratorFragment() {
//Override methods and add assertations here.
};
Fragment frag = startFragment(fragment);
}
}
The startFragment() method adds a fragment you specify to the ViewGroup in the TestActivity.
The good thing about testing fragments, as opposed to Activities, is that you can extends the Fragment to override protected fields and methods within which you can add assertions.
NOTE: Call getSupportFragmentManager() if you are using the support library.
I managed to write two test cases in my XXTest.java with robotium-solo-3.2.1.jar included, luckily in JUnit view it shows the first one is done, which the device exactly worked (on emulator too).
Then it proceed to the second one, but it just hanging there forever! sorry I can't attach screen shot with my account.
here are my code:
public class XXTest extends ActivityInstrumentationTestCase2<SignInActivity> {
private Solo solo;
private Activity mActivity;
private static final String account = "someone";
private static final String pwd = "123456";
#SuppressWarnings("deprecation")
public XXTest() {
super("com.acompany.android", SignInActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
this.mActivity = getActivity();
solo = new Solo(getInstrumentation(), mActivity);
}
#Smoke
public void testLogIn() throws Exception {
EditText accountInput = (EditText) solo.getView(R.id.edit_account);
EditText pwdInput = (EditText) solo.getView(R.id.edit_password);
solo.clearEditText(accountInput);
solo.clearEditText(pwdInput);
solo.enterText(accountInput, account);
solo.enterText(pwdInput, pwd);
solo.clickOnButton(mActivity.getResources()
.getString(R.string.text_sign_in));
solo.waitForActivity("MainActivity");
solo.assertCurrentActivity("Expect MainActivity shown...", "MainActivity");
boolean expected = true;
boolean actual = solo.searchButton(mActivity.getResources().getString(
R.string.welcome_dialog_start));
assertEquals("find friend dialog not shown", expected, actual);
}
#Smoke
public void testOpenPref() throws Exception {
solo.goBack();
solo.clickOnMenuItem(mActivity.getResources().getString(
R.string.text_preferences));
solo.assertCurrentActivity("Expected PrefActivity..", "PrefActivity");
solo.goBackToActivity("MainActivity");
solo.assertCurrentActivity("Expected MainActivity..", "MainActivity");
}
protected void tearDown() throws Exception {
super.tearDown();
}
}
I've searched the sample of NotePadTest.java from Robotium tutorial, those 3 test cases in it are just work fine!
Please tell me where goes wrong?? Am I missing something somewhere? why the second test case not running?
btw. Can there be more than one class extends ActivityInstrumentationTestCase2 in a test project? curious!
You need to use solo.finishOpenedActivities() in your tearDown().
#Robert - this is the issue with Activity testing itself , not to robotium specific .
For the first test method:
the basic flow you is like below:
1>in the setUp() method load the main activity (say MainActivity) > do some ops in your testMethod1() - that results to land you in another activity ( say AnotherActivity) > and in tearDown() you kill the launched activity in setUp() method which is MainActivity
note: but AnotherActivity remains live
For the second test method:
the basic flow you is like below:
2>in the setUp() method try loading the main activity (say MainActivity) again ,although the previously launched AnotherActivity is not yet got killed, so it hangs there , It doesnt even enter the testMethod2() fr execution -
note: the eclipse graphical runner shows it hangs while the execution marker points to the testMethod2() , people thinks that it got stuck in testMethod2() - but the problem lies in setUp() for testMethod2() not in testMethod2() itself.
Solution:
1>Just keep a look on your cross activity navigation - at the end of each testMethod() use solo.goBack() to come back to the same main activity MainActivity from your current Activity ( you got landed in because of your actions)
results - at the end of testMethod1() only the main activity which was opened in setUP() remains. So it gets killed successfully .No Activity remains alive and for the testMethod2().The setUP() is able to load the MainActivity again without a hang - and testMethod2() gets executed .
hope it clarifies :)