Robolectric: Issue Related to Main Looper when using Robolectric 4.3 with Lopper(mode.paused)

Created on 15 Nov 2019  路  4Comments  路  Source: robolectric/robolectric

Hi I am getting Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadow(getMainLooper()).idle() call when using @LooperMode(Mode.PAUSED), if i don't use it takes forever to execute the test case or validate them

i have a class that have normal setup for pojos and set up a adapter on recycler view.

when i run test case through the class it worked fine, but if run them all of the classes togther random unit test cases get failed. even i am using the shadowOf(getMainLooper()).idle(); the test cases get always failed with the Looper error

```
@Test
public void orders_List_Is_Populated() {

    try {

       // Looper.getMainLooper();
        assertNotNull(recyclerView);
        assertNotNull(deliveriesAdapter);
        assertEquals(deliveriesAdapter.getItemCount(), 3);

        recyclerView.setLayoutManager(new LinearLayoutManager(deliveriesFragment.getContext()));

        DeliveriesAdapter.DViewHolder viewHolder = deliveriesAdapter.onCreateViewHolder(recyclerView, 0);
        deliveriesAdapter.onBindViewHolder(viewHolder, 0);

        shadowOf(getMainLooper()).idle();

        assertEquals("Pending", viewHolder.getTextPaymentStatus().toString());
    } catch (Exception ex) {
        fail();
    }
}
### Stack Trace of the above function


java.lang.Exception: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.

at org.robolectric.android.internal.AndroidTestEnvironment.checkStateAfterTestFailure(AndroidTestEnvironment.java:470)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:548)
at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.fail(Assert.java:95)
at com.mastercard.labs.kionect.DeliveriesFragmentTest.orders_List_Is_Populated(DeliveriesFragmentTest.java:163)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:546)
... 6 more
```

Robolectric 4.3 & Androidx

@RunWith(RobolectricTestRunner.class)
@LooperMode(PAUSED)
public class DeliveriesFragmentTest extends BaseRobolectricTest {

private DeliveriesFragment deliveriesFragment;
private Map params;
private String status = "";
private RecyclerView recyclerView;
private Button button_submit;
private SessionManager sessionManager;
private Menu menu;
private MenuItem menuItem;
private Gson gson;
private MainActivity mainActivity;
private List<SalesOrder> salesOrders;
private  SQLiteDatabase db;

@Mock
private FetchOrdersTask fetchOrdersTask;

private SalesOrder salesOrderPaid, salesOrderPending, salesOrderPartial;
private DeliveriesAdapter deliveriesAdapter;

@Before
public void setUp() throws Exception {

    ///Set up Mockito
    MockitoAnnotations.initMocks(this);

    Context mContext = ApplicationProvider.getApplicationContext();
    AppDatabase.getInstance(mContext);

    params = new HashMap();
    status = Constants.PENDING;
    sessionManager = new SessionManager(InstrumentationRegistry.getInstrumentation().getTargetContext());

    mainActivity = Robolectric.setupActivity(MainActivity.class);

    TestFragmentFactory factory = new TestFragmentFactory();

    FragmentScenario.launch(DeliveriesFragment.class, null, factory);

    deliveriesFragment = factory.deliveriesFragment;
    recyclerView = deliveriesFragment.getView().findViewById(R.id.delivery_recycler_view);

    button_submit = deliveriesFragment.getView().findViewById(R.id.button_submit);


    //Create some SalesOrder objects that will be displayed on this fragment
    salesOrderPaid = new SalesOrder();
    salesOrderPaid.setPaymentStatus(CommonHelper.getPaymentStatus(Constants.PAID));

    salesOrderPending = new SalesOrder();
    salesOrderPending.setPaymentStatus(CommonHelper.getPaymentStatus(Constants.PENDING));

    salesOrderPartial = new SalesOrder();
    salesOrderPartial.setPaymentStatus(CommonHelper.getPaymentStatus(Constants.PARTIAL));

    salesOrders = new ArrayList<SalesOrder>(Arrays.asList(salesOrderPaid, salesOrderPending, salesOrderPartial));

    //Create an instance of the DeliveriesAdapter
    deliveriesAdapter = new DeliveriesAdapter(salesOrders, deliveriesFragment);
    recyclerView.setAdapter(deliveriesAdapter);
}


@Test
public void fragmentIsNotNull() {
    assertNotNull(deliveriesFragment);
}

//TODO : Modified these test to call from Enter DeliveriesFragment and check the response.

@Test
public void doInBackground_CanFetchOrders()  {
    try {
        fetchOrdersTask = mock(FetchOrdersTask.class);
        //Stubbing appears before the actual execution
        when(fetchOrdersTask.get()).thenReturn(Constants.SAMPLE_SUCCESSFUL_ORDER_JSON);

        fetchOrdersTask.execute();
        assertEquals(fetchOrdersTask.get(), Constants.SAMPLE_SUCCESSFUL_ORDER_JSON);

    } catch (Exception ex) {
        fail("shouldn't throw error "+ ex);
    }
}

@Test
public void orders_List_Is_Populated() {

    try {

       // Looper.getMainLooper();
        assertNotNull(recyclerView);
        assertNotNull(deliveriesAdapter);
        assertEquals(deliveriesAdapter.getItemCount(), 3);

        recyclerView.setLayoutManager(new LinearLayoutManager(deliveriesFragment.getContext()));

        DeliveriesAdapter.DViewHolder viewHolder = deliveriesAdapter.onCreateViewHolder(recyclerView, 0);
        deliveriesAdapter.onBindViewHolder(viewHolder, 0);

        shadowOf(getMainLooper()).idle();

        assertEquals("Pending", viewHolder.getTextPaymentStatus().toString());
    } catch (Exception ex) {
        fail();
    }
}

@Test
public void orders_List_Is_Not_Populated() {
    try {

        assertNotNull(recyclerView);
        assertNotNull(deliveriesAdapter);

        recyclerView.setLayoutManager(new LinearLayoutManager(deliveriesFragment.getContext()));

        DeliveriesAdapter.DViewHolder viewHolder = deliveriesAdapter.onCreateViewHolder(recyclerView, 0);
        deliveriesAdapter.onBindViewHolder(viewHolder, 0);

        assertNotEquals(deliveriesAdapter.getItemCount(), 3);

        assertNull(viewHolder.getTextPaymentStatus().toString());
        assertNull(viewHolder.getTextShopName().toString());
    } catch (Exception ex) {
        fail();
    }
}


@Test
public void filter_Orders_By_Payment_Status()  {

        assertNotNull(recyclerView);
        assertNotNull(deliveriesAdapter);
        assertEquals(deliveriesAdapter.getItemCount(), 3);

        recyclerView.setLayoutManager(new LinearLayoutManager(deliveriesFragment.getContext()));

        DeliveriesAdapter.DViewHolder viewHolder = deliveriesAdapter.onCreateViewHolder(recyclerView, 0);
        deliveriesAdapter.onBindViewHolder(viewHolder, 0);

        ShadowActivity shadowActivity = shadowOf(mainActivity);

        assertTrue(shadowActivity.getOptionsMenu().hasVisibleItems());
        menuItem = shadowActivity.getOptionsMenu().findItem(R.id.spinner_status);

        assertNotNull(menuItem);

        //Click menu
        shadowActivity.clickMenuItem(R.id.spinner_status);

        assertEquals(salesOrderPending.getPaymentStatus(), viewHolder.getTextPaymentStatus().toString());
}

@Test
public void Single_Item_Click_Should_Launch_Order_Details() {

    try {

        assertNotNull(recyclerView);
        assertNotNull(deliveriesAdapter);
        assertEquals(deliveriesAdapter.getItemCount(), 3);

        recyclerView.setLayoutManager(new LinearLayoutManager(deliveriesFragment.getContext()));

        DeliveriesAdapter.DViewHolder viewHolder = deliveriesAdapter.onCreateViewHolder(recyclerView, 0);
        deliveriesAdapter.onBindViewHolder(viewHolder, 0);

        assertNotNull(recyclerView.findViewHolderForAdapterPosition(0).itemView);

        //Perform click on the single item displayed on the list
        recyclerView.findViewHolderForAdapterPosition(0).itemView.performClick();

        shadowOf(getMainLooper());
        ShadowActivity shadowActivity = shadowOf(deliveriesFragment.getActivity());
        Intent startedIntent = shadowActivity.getNextStartedActivity();
        ShadowIntent shadowIntent = shadowOf(startedIntent);

        assertEquals(ReviewOrderDeliveryActivity.class.getName(), shadowIntent.getIntentClass().getName());
    } catch (Exception ex) {
        fail();
    }

}

@Test
public void register_Merchant() {
    try {
        assertNotNull(button_submit);
        button_submit.performClick();

        ShadowActivity shadowActivity = shadowOf(deliveriesFragment.getActivity());
        Intent startedIntent = shadowActivity.getNextStartedActivity();
        ShadowIntent shadowIntent = shadowOf(startedIntent);

        assertEquals(RegisterTraderFragment.class.getName(), shadowIntent.getIntentClass().getName());
    } catch (Exception ex) {
        fail();
    }

}

Most helpful comment

Hey @anurag1991 .
I had the same problem with Robolectric=4.3 and sdk=28
I solved it by Using new looping mode for versions 4.3 onwards

Currently Robolectric will default to LooperMode.LEGACY behavior, but this can be overridden
by applying a @LooperMode(NewMode) annotation to a test package, test class, or test method, or
via the 'robolectric.looperMode' system property.

So I tagged my test class with LooperMode.Mode.PAUSED and used shadowOf(Looper.getMainLooper()).idle() to sync my tests.

@RunWith(AndroidJUnit4::class)
@LooperMode(LooperMode.Mode.PAUSED)
class FullDialogTest {

   @get:Rule val activityRule = ActivityScenarioRule(MyDrawerActivity::class.java)

    lateinit var dialog: RRoverFullDialog

    @Before fun setUp() {
        dialog = RRoverFullDialog.build()
    }

    @Test fun setTitleTest() {
        activityRule.scenario.onActivity {
            dialog.title("title")
                    .show(it.supportFragmentManager, "TAG")

           shadowOf(Looper.getMainLooper()).idle()

            assertEquals("title", dialog.title.text)
            assertEquals(View.VISIBLE, dialog.title.visibility)
        }
    }
   ...
}

All 4 comments

Hey @anurag1991 .
I had the same problem with Robolectric=4.3 and sdk=28
I solved it by Using new looping mode for versions 4.3 onwards

Currently Robolectric will default to LooperMode.LEGACY behavior, but this can be overridden
by applying a @LooperMode(NewMode) annotation to a test package, test class, or test method, or
via the 'robolectric.looperMode' system property.

So I tagged my test class with LooperMode.Mode.PAUSED and used shadowOf(Looper.getMainLooper()).idle() to sync my tests.

@RunWith(AndroidJUnit4::class)
@LooperMode(LooperMode.Mode.PAUSED)
class FullDialogTest {

   @get:Rule val activityRule = ActivityScenarioRule(MyDrawerActivity::class.java)

    lateinit var dialog: RRoverFullDialog

    @Before fun setUp() {
        dialog = RRoverFullDialog.build()
    }

    @Test fun setTitleTest() {
        activityRule.scenario.onActivity {
            dialog.title("title")
                    .show(it.supportFragmentManager, "TAG")

           shadowOf(Looper.getMainLooper()).idle()

            assertEquals("title", dialog.title.text)
            assertEquals(View.VISIBLE, dialog.title.visibility)
        }
    }
   ...
}

Hey @anurag1991 .
I had the same problem with Robolectric=4.3 and sdk=28
I solved it by Using new looping mode for versions 4.3 onwards

Currently Robolectric will default to LooperMode.LEGACY behavior, but this can be overridden
by applying a @LooperMode(NewMode) annotation to a test package, test class, or test method, or
via the 'robolectric.looperMode' system property.

So I tagged my test class with LooperMode.Mode.PAUSED and used shadowOf(Looper.getMainLooper()).idle() to sync my tests.

@RunWith(AndroidJUnit4::class)
@LooperMode(LooperMode.Mode.PAUSED)
class FullDialogTest {

   @get:Rule val activityRule = ActivityScenarioRule(MyDrawerActivity::class.java)

    lateinit var dialog: RRoverFullDialog

    @Before fun setUp() {
        dialog = RRoverFullDialog.build()
    }

    @Test fun setTitleTest() {
        activityRule.scenario.onActivity {
            dialog.title("title")
                    .show(it.supportFragmentManager, "TAG")

           shadowOf(Looper.getMainLooper()).idle()

            assertEquals("title", dialog.title.text)
            assertEquals(View.VISIBLE, dialog.title.visibility)
        }
    }
   ...
}

For me it worked just tagging my test class with @LooperMode(LooperMode.Mode.PAUSED)

for me it doesnt worked

for me it doesnt worked

@Akshay0701 sorry, have you solved this failure? I meet same error.

failure info:
`
java.lang.Exception: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.

at org.robolectric.android.internal.AndroidTestEnvironment.checkStateAfterTestFailure(AndroidTestEnvironment.java:502)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:581)
at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:278)
at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)

`

and also it require some .dll file in /user/temp/, have you ever met it?

Was this page helpful?
0 / 5 - 0 ratings