Robolectric test fails when trying to inflate layout that has a TextInputLayout

1.6k views Asked by At
Android Studio 4.0.1
Robolectric 4.3.1

I am writing a robolectric test and the test will fail with a error below. And seems to be related to the TextInputLayout that can't be inflated. If I remove the TextInputLayout the test will pass ok.

This is the error message I am getting.

android.view.InflateException: Binary XML file line #47: Binary XML file line #47: Error inflating class com.google.android.material.textfield.TextInputLayout
Caused by: android.view.InflateException: Binary XML file line #47: Error inflating class com.google.android.material.textfield.TextInputLayout
Caused by: java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.AppCompat (or a descendant).

The test class itself

@Config(sdk = [Build.VERSION_CODES.O_MR1])
@RunWith(AndroidJUnit4::class)
class CharitiesAdapterTest {

    private lateinit var charitiesAdapter: CharitiesAdapter

    @Before
    fun setUp() {
        charitiesAdapter = CharitiesAdapter()
    }

    @Test
    fun `should create viewHolder`() {
        // Act & Assert
        assertThat(createViewHolder()).isNotNull
    }

    private fun createViewHolder(): CharitiesViewHolder {
        val constraintLayout = ConstraintLayout(ApplicationProvider.getApplicationContext())

        return charitiesAdapter.onCreateViewHolder(constraintLayout, 0)
    }
}

The actual adapter under test

class CharitiesAdapter : RecyclerView.Adapter<CharitiesViewHolder>() {

    private val charitiesList: MutableList<Charity> = mutableListOf()
    private var selectedCharity: (Charity) -> Unit = {}

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharitiesViewHolder {
        val charitiesViewHolder = CharitiesViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.charity_item, parent, false))

        return charitiesViewHolder
    }

    override fun getItemCount(): Int = charitiesList.count()

    override fun onBindViewHolder(holder: CharitiesViewHolder, position: Int) {
        // left blank
    }
}

This is the part of the layout that is having the issue. When removing just the TextInputLayout the test will pass OK

 <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayoutPostcode"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ivLogo">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/editTextPostcode"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </com.google.android.material.textfield.TextInputLayout>
    

This is the style I am using

    <resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
</resources>

Thanks for any advice

2

There are 2 answers

6
ggordon On BEST ANSWER

You could configure your tests to use a mock application that uses the app theme as shown below:

Kotlin Implementation

class TestApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        setTheme(R.style.AppTheme) //or just R.style.Theme_AppCompat
    }
}

Then configure your tests to use this mock application

@Config(application=TestApplication::class, sdk = [Build.VERSION_CODES.O_MR1])
@RunWith(AndroidJUnit4::class)
class CharitiesAdapterTest {

Java Implementation

public class TestApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        setTheme(R.style.AppTheme); //or just R.style.Theme_AppCompat
    }
}

Then configure your tests to use this mock application

@Config(application=TestApplication.class, sdk = [Build.VERSION_CODES.O_MR1])
@RunWith(AndroidJUnit4.class)
class CharitiesAdapterTest {
2
Martin Zeitler On

You are passing the wrong Context class: ApplicationProvider.getApplicationContext().

The ApplicationContext has no theme at all, using an Activity's Context suggested (which will always have the current Activity's theme, no matter which Activity or Theme it is). By the code provided it is unclear how you start the Activity (there the Context should be accessible).

Testing with an ActivityScenario or ActivityScenarioRule might be the proper way to do it.
The documentation shows how it works: AndroidX Test & JUnit4 rules with AndroidX Test.