typeText\enterText: This method can not be called from the main application thread. Android Studio 1.0.2, Robotium 5.2.1

628 views
Skip to first unread message

Rizwan Choudrey

unread,
Jan 6, 2015, 7:25:17 AM1/6/15
to robotium-...@googlegroups.com
Hi I have written a simple test for a dialog with an EditText and 3 buttons. Running each test individually (ctrl-shift-f10 on test method), they all work fine. Running them as a batch (ctrl-shift-f10 outside of any method), I get a "This method can not be called from the main application thread" error on solo.typeText(.) in a single test even though all the tests use it (testCancelButton). All the tests are very simple and virtually identical. Same problem if I change to enterText(.). 

Many thanks in advance,
Riz


Error
FATAL EXCEPTION: main
    java.lang.RuntimeException: This method can not be called from the main application thread
            at android.app.Instrumentation.validateNotAppThread(Instrumentation.java:1531)
            at android.app.Instrumentation.runOnMainSync(Instrumentation.java:335)
            at com.robotium.solo.TextEnterer.typeText(TextEnterer.java:75)
            at com.robotium.solo.Solo.typeText(Solo.java:1786)
            at com.chdryra.android.reviewer.test.DialogFragmentGvDataAddCommentTest$2.run(DialogFragmentGvDataAddCommentTest.java:80)
            at android.os.Handler.handleCallback(Handler.java:605)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4424)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:787)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:554)
            at dalvik.system.NativeStart.main(Native Method)




Code
/*
 * Copyright (c) 2015, Rizwan Choudrey - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 * Author: Rizwan Choudrey
 * Date: 5 January, 2015
 */


import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;

import com.chdryra.android.reviewer.ActivityFeed;
import com.chdryra.android.reviewer.Administrator;
import com.chdryra.android.reviewer.ConfigGvDataAddEditDisplay;
import com.chdryra.android.reviewer.ControllerReview;
import com.chdryra.android.reviewer.DialogFragmentGvDataAdd;
import com.chdryra.android.reviewer.GvCommentList;
import com.chdryra.android.reviewer.GvDataList;
import com.chdryra.android.reviewer.LauncherUI;
import com.chdryra.android.reviewer.test.TestUtils.DialogAddListener;
import com.chdryra.android.testutils.RandomStringGenerator;
import com.robotium.solo.Solo;

/**
 * Created by: Rizwan Choudrey
 * On: 05/01/2015
 */
public class DialogFragmentGvDataAddCommentTest extends
        ActivityInstrumentationTestCase2<ActivityFeed> {
    private static final int    REQUEST_CODE = 1976;
    private static final String DIALOG_TAG   = "TestAddDialog";

    private Solo                                       mSolo;
    private ConfigGvDataAddEditDisplay.AddComment      mDialog;
    private DialogAddListener<GvCommentList.GvComment> mFragment;
    private ControllerReview                           mController;
    private Activity                                   mActivity;

    public DialogFragmentGvDataAddCommentTest() {
        super(ActivityFeed.class);
    }

    @SmallTest
    public void testCancelButton() {
        launchDialogAndTestShowing(false);

        final String tag = RandomStringGenerator.nextWord();
        assertFalse(mSolo.searchEditText(tag));

        mActivity.runOnUiThread(new Runnable() {
            public void run() {
                mSolo.typeText(mSolo.getEditText(0), tag);

                assertTrue(mSolo.searchEditText(tag));
                assertNull(mFragment.getData());

                mSolo.clickOnButton("Cancel");

                assertNull(mFragment.getData());
                assertFalse(dialogIsShowing());
            }
        });
    }

    @SmallTest
    public void testAddButtonNotQuickSet() {
        launchDialogAndTestShowing(false);

        final String tag = RandomStringGenerator.nextSentence();
        assertFalse(mSolo.searchEditText(tag));
        assertEquals(0, getControllerData().size());

        mActivity.runOnUiThread(new Runnable() {
            public void run() {
                mSolo.typeText(mSolo.getEditText(0), tag);

                assertTrue(mSolo.searchEditText(tag));
                assertNull(mFragment.getData());

                mSolo.clickOnButton("Add");

                assertNotNull(mFragment.getData());
                assertEquals(tag, mFragment.getData().getComment());
                assertEquals(0, getControllerData().size());
                assertTrue(dialogIsShowing());
                assertTrue(mSolo.getEditText(0).getText().toString().length() == 0);
            }
        });
    }

    @SmallTest
    public void testDoneButtonNotQuickSet() {
        launchDialogAndTestShowing(false);

        final String tag = RandomStringGenerator.nextWord();
        assertFalse(mSolo.searchEditText(tag));
        assertEquals(0, getControllerData().size());

        mActivity.runOnUiThread(new Runnable() {
            public void run() {
                mSolo.typeText(mSolo.getEditText(0), tag);

                assertTrue(mSolo.searchEditText(tag));
                assertNull(mFragment.getData());

                mSolo.clickOnButton("Done");

                assertNotNull(mFragment.getData());
                assertEquals(tag, mFragment.getData().getComment());
                assertEquals(0, getControllerData().size());
                assertFalse(dialogIsShowing());
            }
        });
    }

    @SmallTest
    public void testQuickSet() {
        assertEquals(0, getControllerData().size());

        launchDialogAndTestShowing(true);

        mActivity.runOnUiThread(new Runnable() {
            public void run() {
                String tag1 = RandomStringGenerator.nextWord();
                mSolo.typeText(mSolo.getEditText(0), tag1);
                mSolo.clickOnButton("Add");

                String tag2 = RandomStringGenerator.nextWord();
                mSolo.enterText(mSolo.getEditText(0), tag2);
                mSolo.clickOnButton("Add");

                String tag3 = RandomStringGenerator.nextWord();
                mSolo.enterText(mSolo.getEditText(0), tag3);
                mSolo.clickOnButton("Done");

                GvCommentList data = (GvCommentList) getControllerData();
                assertEquals(3, data.size());
                assertEquals(tag1, data.getItem(0).getComment());
                assertEquals(tag2, data.getItem(1).getComment());
                assertEquals(tag3, data.getItem(2).getComment());
            }
        });
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mDialog = new ConfigGvDataAddEditDisplay.AddComment();
        mFragment = new DialogAddListener<>();
        FragmentManager manager = getActivity().getFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(mFragment, DIALOG_TAG);
        ft.commit();

        mController = Administrator.get(getInstrumentation().getTargetContext())
                .createNewReviewInProgress();

        mActivity = getActivity();
        mSolo = new Solo(getInstrumentation(), mActivity);
    }

    private void launchDialogAndTestShowing(boolean quickSet) {
        Bundle args = getControllerBundle();
        args.putBoolean(DialogFragmentGvDataAdd.QUICK_SET, quickSet);

        assertFalse(dialogIsShowing());

        LauncherUI.launch(mDialog, mFragment, REQUEST_CODE, DIALOG_TAG, args);
        getInstrumentation().waitForIdleSync();

        assertTrue(dialogIsShowing());
    }

    private boolean dialogIsShowing() {
        return mSolo.searchButton("Cancel") &&
                mSolo.searchButton("Add") &&
                mSolo.searchButton("Done") &&
                mSolo.searchText(GvDataList.GvType.COMMENTS.getDatumString());
    }

    private GvDataList getControllerData() {
        return mController.getData(GvDataList.GvType.COMMENTS);
    }

    private Bundle getControllerBundle() {
        return Administrator.get(getInstrumentation().getTargetContext()).pack(mController);
    }
}

Renas

unread,
Jan 6, 2015, 8:22:07 AM1/6/15
to robotium-...@googlegroups.com
Hi Rizwan,

I think your problem is due to using:  

mActivity.runOnUiThread(new Runnable() {
            public void run() {

In your test methods. Please remove those and try to run the tests again. 

Renas

--
You received this message because you are subscribed to the Google Groups "Robotium Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to robotium-develo...@googlegroups.com.
To post to this group, send email to robotium-...@googlegroups.com.
Visit this group at http://groups.google.com/group/robotium-developers.
For more options, visit https://groups.google.com/d/optout.

Rizwan Choudrey

unread,
Jan 6, 2015, 8:41:44 AM1/6/15
to robotium-...@googlegroups.com
I'm afraid I need them for the tests to run and pass properly i.e. for action-assertion to run sequentially. If I remove them, the assertions are tested before the button is finished being pressed causing the test to fail because the button press has not been carried out.
To unsubscribe from this group and stop receiving emails from it, send an email to robotium-developers+unsub...@googlegroups.com.

Renas

unread,
Jan 6, 2015, 8:50:52 AM1/6/15
to robotium-...@googlegroups.com
Use one of the waitFor methods after pressing the button. You could e.g. use solo.waitForCondition after the button press in order to make sure you are at the required state before continuing with the asserts.  


To unsubscribe from this group and stop receiving emails from it, send an email to robotium-develo...@googlegroups.com.

Rizwan Choudrey

unread,
Jan 6, 2015, 9:16:41 AM1/6/15
to robotium-...@googlegroups.com
OK thanks. TBH that doesn't seem to be a very "robust" way of ensuring things happen in the proper order. Is there a reason why using the Ui thread doesn't work?

--
You received this message because you are subscribed to a topic in the Google Groups "Robotium Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/robotium-developers/hiFhiaVHFkc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to robotium-develo...@googlegroups.com.

Renas

unread,
Jan 6, 2015, 9:22:15 AM1/6/15
to robotium-...@googlegroups.com
The reason you are getting the issue is because typeText/enterText are already run on the main thread. You could move them out of your runOnMainThread and just keep clickOnButton and the asserts there. That should actually work. Please see below.

mSolo.typeText(mSolo.getEditText(0), tag);
 mActivity.runOnUiThread(new Runnable() {
            public void run() {
...


I think waiting for certain conditions and using waitFor methods is actually the most robust way of writing tests. In that case you leave nothing to chance. The following steps will only be executed when they are supposed to. 

Renas

Rizwan Choudrey

unread,
Jan 6, 2015, 9:31:08 AM1/6/15
to robotium-...@googlegroups.com
I suppose what I mean by "not robust" is that it seems Robotium takes a very long time to carry out actions (e.g. show the dialog, enter the text, press the button) and I can't guarantee how long it will take to actually carry out the action. Therefore, I don't know what the appropriate time/condition is to guarantee the action has been carried out. For example, how can I ensure clickOnButton(.) has finished without using a very long waiting time, or without implicitly waiting for a condition that should really be in an assert?

Rizwan Choudrey

unread,
Jan 6, 2015, 9:31:08 AM1/6/15
to robotium-...@googlegroups.com
I presume the reason they pass as individual tests but fail in a batch is because only 1 thread is necessary for a single test but multiple ones are fired off in a batch?

Renas

unread,
Jan 6, 2015, 9:43:10 AM1/6/15
to robotium-...@googlegroups.com
The click method is finished when the next method in the test case is executed (they are run in the same thread). What happens though in your case is that even though the click has just happened your assert runs to fast for the UI and your fragment to be in the right state. So normally the default timeout of 20 seconds for the waitFor methods is plenty (otherwise it would be a very bad user experience). However some times one needs to wait for the app to fetch things etc and in that case its recommended to set a higher timeout value. 

Renas

unread,
Jan 6, 2015, 9:43:35 AM1/6/15
to robotium-...@googlegroups.com
That is probably the reason why it happens. 

Rizwan Choudrey

unread,
Jan 6, 2015, 2:13:48 PM1/6/15
to robotium-...@googlegroups.com
Hi just wanted to update with a solution that works for me (took me all day to figure it out so hopefully it may help someone!). 

It seems the weird behaviour I was getting (different results depending on release/debug versions, Solo hanging indefinitely when asked to do something, sometimes null pointers sometimes not) was because I was using private member variables which weren't final in the anonymous inner class declarations for the Runnables for doing stuff on the UI thread. Unfortunately these can't be made final as they are assigned in setup() rather than in the constructor, So I assigned new final variables in each test representing the member variables and rejigged some of the private methods to take final parameters rather than reference the member variables directly. I then used only the final versions of these variables/methods within the Runnables.

The tests and Robotium now perform consistently both individually as tests and in batch runs. 
To unsubscribe from this group and stop receiving emails from it, send an email to robotium-developers+unsubscribe...@googlegroups.com.
To post to this group, send email to robotium-...@googlegroups.com.
Visit this group at http://groups.google.com/group/robotium-developers.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Robotium Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to robotium-developers+unsub...@googlegroups.com.
To post to this group, send email to robotium-...@googlegroups.com.
Visit this group at http://groups.google.com/group/robotium-developers.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Robotium Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/robotium-developers/hiFhiaVHFkc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to robotium-developers+unsub...@googlegroups.com.

To post to this group, send email to robotium-...@googlegroups.com.
Visit this group at http://groups.google.com/group/robotium-developers.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Robotium Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to robotium-developers+unsub...@googlegroups.com.
To post to this group, send email to robotium-...@googlegroups.com.
Visit this group at http://groups.google.com/group/robotium-developers.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Robotium Developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/robotium-developers/hiFhiaVHFkc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to robotium-developers+unsub...@googlegroups.com.

To post to this group, send email to robotium-...@googlegroups.com.
Visit this group at http://groups.google.com/group/robotium-developers.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages