#android #robolectric #android-textinputlayout #instrumented-test
#Android #robolectric #android-textinputlayout #инструментальный-тест
Вопрос:
Android Studio 4.0.1
Robolectric 4.3.1
Я пишу тест robolectric, и тест завершится с ошибкой ниже. И, похоже, связан с TextInputLayout, который нельзя раздуть. Если я удалю TextInputLayout, тест пройдет нормально.
Это сообщение об ошибке, которое я получаю.
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).
Сам тестовый класс
@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 amp; Assert
assertThat(createViewHolder()).isNotNull
}
private fun createViewHolder(): CharitiesViewHolder {
val constraintLayout = ConstraintLayout(ApplicationProvider.getApplicationContext())
return charitiesAdapter.onCreateViewHolder(constraintLayout, 0)
}
}
Фактический тестируемый адаптер
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
}
}
Это та часть макета, в которой возникает проблема. При удалении только TextInputLayout тест пройдет нормально
<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>
Это стиль, который я использую
<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>
Спасибо за любые советы
Ответ №1:
Вы можете настроить свои тесты на использование макетного приложения, которое использует тему приложения, как показано ниже:
Реализация Kotlin
class TestApplication : Application() {
override fun onCreate() {
super.onCreate()
setTheme(R.style.AppTheme) //or just R.style.Theme_AppCompat
}
}
Затем настройте свои тесты на использование этого макетного приложения
@Config(application=TestApplication::class, sdk = [Build.VERSION_CODES.O_MR1])
@RunWith(AndroidJUnit4::class)
class CharitiesAdapterTest {
Реализация Java
public class TestApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
setTheme(R.style.AppTheme); //or just R.style.Theme_AppCompat
}
}
Затем настройте свои тесты на использование этого макетного приложения
@Config(application=TestApplication.class, sdk = [Build.VERSION_CODES.O_MR1])
@RunWith(AndroidJUnit4.class)
class CharitiesAdapterTest {
Комментарии:
1. Не уверен, что установка темы для приложения что-либо изменит, потому что у него нет представлений (у приложения нет темы, потому что ему не требуется тема без каких-либо представлений для оформления).
R.style.AppTheme
скорее тема по умолчанию для всехActivity
, но не дляApplication
.2. Ну и что, если их два
Activity
, один из которых используетTheme.MaterialComponents.Light.DarkActionBar
илиNoActionBar
, а другой использует тему, которая не работает. Как ваш тестовый подход сможет обнаружить проблему? См. Мой подход к тестированию с фактическими темами, как определено вAndroidManifest.xml
.3. Спасибо за последующие вопросы, я протестировал это локально, с изменениями и без изменений перед публикацией, чтобы убедиться, что это сработало. Кажется, просто требуется тема и никаких действий.
4. Он тестирует адаптер Charities, а не то, как адаптер работает в конкретном действии. Он мог бы написать отдельный тест для этого действия, и этот тест выявил бы любые проблемы, связанные с этим действием, и то, как адаптер выполняет действие, что позволяет разделить проблемы.
5. @ggordon Это правильно. Я тестирую CharitiesAdapter только как модульный тест, предназначенный только для stand. Я удалил неважную логику привязки представлений к некоторым фиктивным данным, поскольку я хотел, чтобы фрагменты были короткими для этого вопроса. Я уже тестировал подобное раньше с другими адаптерами. Тем не менее, я никогда не тестировал адаптер, который имеет
TextInputLayout
в своем макете.
Ответ №2:
Вы передаете неправильный Context
класс : ApplicationProvider.getApplicationContext()
.
У ApplicationContext
него вообще нет темы, используя Activity
Context
предложенную (которая всегда будет иметь текущую Activity
тему, независимо от того, какая Activity
или Theme
какая). Из предоставленного кода неясно, как вы запускаете Activity
(там Context
должно быть доступно).
Тестирование с ActivityScenario
помощью or ActivityScenarioRule
может быть правильным способом сделать это.
В документации показано, как это работает: AndroidX Test amp; JUnit4 правила с AndroidX Test.
Комментарии:
1. Это всего лишь тест адаптера recyclerview. Поэтому я на самом деле не начинаю никаких действий, просто используя robolectric для раздувания макета. Спасибо
2. Издевательство над этим
Context
проблематично, потому что тест может пройти, даже если приложение выйдет из строя. При выполнении интеграционных тестов рекомендуется предположить, что адаптер работает, пока есть доступные элементы. При тестировании более одного компонента (что вы и делаете) это больше не модульное тестирование (хотя довольно бессмысленно проводить модульное тестирование библиотек Google, которые обычно уже хорошо протестированы). Это было бы что-то совсем другое, когда вы тестировали пользовательскую реализацию такого компонента.