Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instrumented Unit Class Test - Can't create handler inside thread that has not called Looper.prepare()

Sorry I've looked at tutorials all over the place and not found the answer I'm looking for. I'm following Google's Tutorial at the moment here: https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html

I'm trying to create an Instrumented test and when I run it I'm getting the error : java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

So my test is as follows:

    package testapp.silencertestapp;

import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class MainActivityTest {

    private MainActivity testMain;

    @Before
    public void createActivity(){
        testMain = new MainActivity();
    }

    @Test
    public void checkFancyStuff(){
        String time = testMain.createFancyTime(735);

        assertThat(time, is("07:35"));
    }
}

And I'm trying to run a method inside the main activity is as follows (this is an extract):

public class MainActivity extends AppCompatActivity {

    private TimePicker start;
    private TimePicker end;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        start = (TimePicker) findViewById(R.id.startPicker);
        end = (TimePicker) findViewById(R.id.endPicker);
}

 String createFancyTime(int combinedTime) {

        StringBuilder tempString = new StringBuilder(Integer.toString(combinedTime));
        if(tempString.length()==4){
            tempString = tempString.insert(2, ":");
        }
        else if (tempString.length()==3){
            tempString = tempString.insert(1, ":");
            tempString = tempString.insert(0, "0");
        }
        else if(tempString.length()==2){
            tempString = tempString.insert(0, "00:");
        }
        else if(tempString.length()==1){
            tempString = tempString.insert(0, "00:0");
        }
        return tempString.toString();
    }

I presume that this is an issue because I'm not starting the service up correctly or something - I've tried using several ways, but I just get errors everywhere. Searching on here and this error is popular, but not in relation to testing, so wondered if anyone could kindly point me in the right direction so I can test the methods in this class?

like image 768
Graeme Wilkinson Avatar asked Sep 27 '17 23:09

Graeme Wilkinson


2 Answers

This is how I solved it. I ran test case on main thread using runOnMainSync. Here is the comlete solution:

@RunWith(AndroidJUnit4::class)
class AwesomeViewModelTest {

    @Test
    fun testHandler() {

        getInstrumentation().runOnMainSync(Runnable {
            val context = InstrumentationRegistry.getInstrumentation().targetContext

          // Here you can call methods which have Handler

       
        })
    }


}
like image 166
Hitesh Sahu Avatar answered Nov 09 '22 23:11

Hitesh Sahu


The error is happening because the Looper is not correctly set up for your system under test (MainActivity).

While Activity, Fragment etc. have no-args constructors, they are designed to be instantiated by the Android OS and as such calling MainActivity = new Activity() is not enough to obtain a fully-operational death star instance complete with Handler and Looper.

There are two options if you want to proceed with testing:

  1. If you want to test a real instance of an Activity it will have to be an instrumented unit test (androidTest not test) and the @TestRule will cause the Android OS to correctly instantiate an instance of Activity:

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule =
        new ActivityTestRule(MainActivity.class);
    
  2. If you would prefer to keep writing local unit tests that run in the IDE you can use Robolectric. Robolectric will correctly stub behaviour on a shadow Activity so that you can test components that rely on Looper etc. Note there is some setup involved.

like image 37
David Rawson Avatar answered Nov 10 '22 00:11

David Rawson