Running Android ActivityUnitTestCase results in "RuntimeException: could not find test class" - android

Trying to generate a simple ActivityUnitTestCase to test various Fragments in my Android code.
public class MenuFragmentTest extends ActivityUnitTestCase<FragmentAdapter> {
static {
FragmentAdapter.setFragmentUnderTest(new MenuFragment());
}
public MenuFragmentTest() {
super(FragmentAdapter.class);
}
public void testMenuOptions() {
System.out.println(getActivity().findViewById(1));
}
}
The generalized FragmentAdapter that I'm trying to use to test Fragments in the app:
public class FragmentAdapter extends FragmentActivity {
static Fragment fragmentUnderTest;
public static void setFragmentUnderTest(Fragment fragmentUnderTest) {
FragmentAdapter.fragmentUnderTest = fragmentUnderTest;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = new LinearLayout(this);
ll.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
setContentView(ll);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragmentUnderTest, null);
fragmentTransaction.commit();
}
}
After the main app installs successfully, the Android JUnit Test Runner fails with
java.lang.RuntimeException: Could not find test class. Class: com.XXX.core.MenuFragment
Why can the Test Runner not find the test class I am running?

Apparently the name of the test package really does matter:
From http://developer.android.com/tools/testing/testing_android.html:
If the application under test has a package name of com.mydomain.myapp, then the Android tools set the test package name to com.mydomain.myapp.test
Altering the package instrumentation targetPackage attribute is not good enough to allow you to create tests in the same package as the activity under test, even if the test is in the separate test project.

Related

Robolectric 3.0 Nullpointer on DefaultPackageManager.getActivityInfo()

I try to create a Robolectric test (3.0-rc02) for the following Activity:
public class NotificationActivity extends ActionBarActivity {
private NotificationFragment fragment;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification);
}
}
The test is looking like this:
#Config(manifest = IConfig.MANIFEST_PATH, emulateSdk = IConfig.SDK_VERSION, reportSdk = IConfig.SDK_VERSION)
#RunWith(RobolectricTestRunner.class)
public class AbstractFragmentTest {
#Test
public void test() {
Robolectric.buildActivity(NotificationActivity.class).setup().visible();
}
where SDK_VERSION = 18.
When running the test, I get this error:
java.lang.NullPointerException
at org.robolectric.res.builder.DefaultPackageManager.getActivityInfo(DefaultPackageManager.java:164)
at org.robolectric.util.ActivityController.getActivityInfo(ActivityController.java:65)
at org.robolectric.util.ActivityController.attach(ActivityController.java:51)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:121)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:309)
at org.robolectric.shadows.CoreShadowsAdapter$2.runPaused(CoreShadowsAdapter.java:47)
at org.robolectric.util.ActivityController.create(ActivityController.java:118)
at org.robolectric.util.ActivityController.create(ActivityController.java:129)
at org.robolectric.util.ActivityController.setup(ActivityController.java:210)
at com.viae.common.view.AbstractFragmentTest.test(AbstractFragmentTest.java:31)
Anyone knows what I'm doing wrong over here?
Issue solved
Using FragmentActivity in stead of ActionBarActivity did the trick to me.
My main logic is in a fragment (DialogFragment) inside the activity. Both activities are supporting the DialogFragment, so for my functional tests it doesn't matter which of the 2 parent activities I use.
ActionBarActivity is deprecated. Use AppCompatActivity instead

Testing Android Components With Espresso

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.

JUnit testing for Android app with fragments

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.

ActivityMock for Fragment in test project

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

How can I test fragments with Robolectric?

I know there is a Robolectric.shadowOf(Fragment) method and a ShadowFragment class, thought they aren't listed on the docs, but I can't make it work.
myFragment = new MyFragment();
myFragment.onCreateView(LayoutInflater.from(activity), (ViewGroup) activity.findViewById(R.id.container), null);
myFragment.onAttach(activity);
myFragment.onActivityCreated(null);
I'm working with API level 13 (Honeycomb).
Thanks.
Edit #4 & #5: In Robolectric 3.*, they split up the fragment starting functions.
For support fragments, you will need to add a dependency to your build.gradle:
testCompile "org.robolectric:shadows-supportv4:3.8"
Import: org.robolectric.shadows.support.v4.SupportFragmentTestUtil.startFragment;
For platform fragments, you don't need this dependency. Import: import static org.robolectric.util.FragmentTestUtil.startFragment;
They both use the same name of startFragment().
import static org.robolectric.shadows.support.v4.SupportFragmentTestUtil.startFragment;
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class)
public class YourFragmentTest
{
#Test
public void shouldNotBeNull() throws Exception
{
YourFragment fragment = YourFragment.newInstance();
startFragment( fragment );
assertNotNull( fragment );
}
}
Edit #3: Robolectric 2.4 has an API for support and regular fragments. You can either use the newInstance() pattern or use the constructor when constructing your Fragment's.
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertNotNull;
import static org.robolectric.util.FragmentTestUtil.startFragment;
#RunWith(RobolectricGradleTestRunner.class)
public class YourFragmentTest
{
#Test
public void shouldNotBeNull() throws Exception
{
YourFragment fragment = new YourFragment();
startFragment( fragment );
assertNotNull( fragment );
}
}
Edit #2: There's a new helper if you're using support fragments (one that supports regular activities/fragments should be in the next release):
import static org.robolectric.util.FragmentTestUtil.startFragment;
#Before
public void setUp() throws Exception
{
fragment = YourFragment.newInstance();
startFragment( fragment );
}
Edit: If you upgraded to Robolectric 2.0:
public static void startFragment( Fragment fragment )
{
FragmentActivity activity = Robolectric.buildActivity( FragmentActivity.class )
.create()
.start()
.resume()
.get();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add( fragment, null );
fragmentTransaction.commit();
}
Original answer
As the other commenter suggested, you do need to use the fragment manager (instead of calling the lifecycle methods you listed above).
#RunWith(MyTestRunner.class)
public class YourFragmentTest
{
#Test
public void shouldNotBeNull() throws Exception
{
YourFragment yourFragment = new YourFragment();
startFragment( yourFragment );
assertNotNull( yourFragment );
}
I create a test runner and have a function that starts up a fragment for me so I can use it everywhere.
public class MyTestRunner extends RobolectricTestRunner
{
public MyTestRunner( Class<?> testClass ) throws InitializationError
{
super( testClass );
}
public static void startFragment( Fragment fragment )
{
FragmentManager fragmentManager = new FragmentActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add( fragment, null );
fragmentTransaction.commit();
}
}
You guys are all doing this the hard way. Just use FragmentTestUtil.
FragmentTestUtil.startFragment(yourfragment);
Support fragments have been moved to module:
shadows-support-v4
(as of July,2015, Robolectric v3.0)
Add a gradle dependency to app/build.gradle:
testCompile 'org.robolectric:shadows-support-v4:3.0'
Then import to your Robolectric test java class:
import org.robolectric.shadows.support.v4.SupportFragmentTestUtil;
Then you can start & use a support-v4 fragment for testing:
#Test
public void minimalFragmentTest() throws Exception {
MyFunFragment fragment = new MyFunFragment();
SupportFragmentTestUtil.startVisibleFragment(fragment);
assertThat(fragment.getView()).isNotNull();
}
References:
github changelog, moving support fragments to different module
Old android fragments are already deprecated, seems like support fragments soon will be deprecated too. To test androidx fragments you can use fragment scenarios with robolectric https://developer.android.com/training/basics/fragments/testing
testImplementation 'androidx.fragment:fragment-testing:1.2.2'
val scenario = launchFragmentInContainer<MyFragment>()
scenario.onFragment { fragment ->
assertNotNull(fragment.view.synteticInflatedView)
}
I'm pretty sure you have to create a FragmentTransaction using the FragmentManager, then it will work.
I just wanted to add that in Robolectric 2.0 even after doing:
activity = Robolectric.buildActivity(FragmentActivity.class).create().start().resume().get();
fragment.show(activity.getSupportFragmentManager(), null);
fragment.getDialog(); //This stills returns null
It still returned null for me. what I did was to add activity.getSupportFragmentManager().executePendingTransaction(); and it worked.
It seems robolectric doesn't run this for some reason. it seems that maybe the Looper is paused or something. any way this worked for me and it looks like this:
activity = Robolectric.buildActivity(FragmentActivity.class).create().start().resume().get();
fragment.show(activity.getSupportFragmentManager(), null);
activity.getSupportFragmentManager().executePendingTransactions();
fragment.getDialog();
SupportFragmentTestUtil.startFragment(fragment, AppCompatActivity::class.java)
If the activity is extending AppCompatActivity
This is using Kotlin

Categories

Resources