Миграция тестов Junit4 на androidx: по каким причинам "не удалось загрузить программу-участник"?
Я перевожу свое приложение на androidx, но я не могу заставить свои модульные тесты работать. Я взял пример из Google AndroidJunitRunnerSample, который был обновлен для использования нового Androidroid API. Я получаю следующую ошибку при попытке запустить мои тесты:
java.lang.Exception: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded. Check your build configuration.
Вот мой модуль build.gradle:
android {
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
// Test dependencies
androidTestImplementation 'androidx.test:core:1.0.0-beta02'
androidTestImplementation 'androidx.test.ext:junit:1.0.0-beta02'
androidTestImplementation 'androidx.test:runner:1.1.0-beta02'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02'
androidTestImplementation "androidx.arch.core:core-testing:2.0.0"
androidTestImplementation 'androidx.room:room-testing:2.1.0-alpha01'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02'
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
}
И вот как мои тесты структурированы:
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@RunWith(AndroidJUnit4.class)
public class EntityParcelTest {
@BeforeClass
public void createEntities() {
// Setup...
}
@Test
void someTest() {
// Testing here
}
Что я делаю неправильно?
21 ответ
Удаление @RunWith(AndroidJUnit4.class)
аннотации из тестовых классов устранили проблему, хотя я не могу точно сказать, почему или как она это исправила.
Изменить: Хорошо, я сделал еще несколько испытаний. Я перенес свое приложение в Kotlin, и вдруг я заметил, что тесты начали работать с @RunWith
аннотации тоже. Вот что я узнал:
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@RunWith(AndroidJUnit4.class) // <-- @RunWith + @BeforeClass = Error
public class AndroidXJunitTestJava {
@BeforeClass
public static void setup() {
// Setting up once before all tests
}
@Test
public void testing() {
// Testing....
}
}
Этот тест Java не удается с Delegate runner for AndroidJunit4 could not be loaded
ошибка. Но если я удалю @RunWith
аннотация, это работает. Кроме того, если я заменю @BeforeClass
настроить только с @Before
, как это:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@RunWith(AndroidJUnit4.class) // <-- @RunWith + @Before = works?
public class AndroidXJunitTestJava {
@Before
public void setup() {
// Setting up before every test
}
@Test
public void testing() {
// Testing....
}
}
Тесты будут работать без ошибок. Мне нужно было использовать @BeforeClass
аннотации, поэтому я просто удалил @RunWith
,
Но теперь, когда я использую Kotlin, работает следующее (которое должно быть равным первому примеру Java):
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AndroidXJunitTest {
companion object {
@BeforeClass fun setup() {
// Setting up
}
}
@Test
fun testing() {
// Testing...
}
}
Кроме того, как сказал Алессандро Биссек в ответе и @Ioane Sharvadze в комментариях, такая же ошибка может произойти с @Rule
аннотаций. Если я добавлю строку
@Rule val instantTaskExecutorRule = InstantTaskExecutorRule()
В примере с Kotlin возникает та же самая ошибка участника делегата. Это должно быть заменено
@get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule()
Объяснение здесь.
Вы также получите сообщение об ошибке, если вы используете тестовое правило в Kotlin и напишите его в стиле Java
@Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java)
Вы должны изменить @Rule
в @get:Rule
@get:Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java)
Сообщение об ошибке отображается при удалении метода @Test.
Вы можете попробовать поставить метод @Test и запустить
@Test
public void signInTest() {
}
Во время тестирования активности убедитесь, что переменная ActivityTestRule является общедоступной:
@Rule
public ActivityTestRule<YourActivity> activityTestRule = new ActivityTestRule<>(YourActivity.class);
Есть два способа решить эту проблему:
Решение 1:
Сборка -> Чистый проект. Затем Build -> Rebuild Project
Решение 2:
Есть 2 пакета с AndroidJUnit4
androidx.test.runner
(не рекомендуется)androidx.test.ext.junit.runners
Убедитесь, что в вашем build.gradle (приложении) есть эта строка
android {
defaultConfig {
....
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
....
}
}
и убедитесь, что вы используете (1) (который устарел) вместо (2) (новый, но еще не стабильный) в @RunWith(AndroidJUnit4.class)
перед тестовым классом.
Изменения
@Test
void someTest() {
// Testing here
}
в
@Test
public void someTest() {
// Testing here
}
работает для меня.
AndroidJUnit4 НЕ ПОДДЕРЖИВАЕТ функции с@Test
аннотации ни в классе, ни в суперклассе:
- параметры:
@Test fun dontWork(param: String) {}
- модификаторы частного доступа:
@Test private fun dontWork2() {}
Примечание: без @Test
аннотации, указанные выше, разрешены
Ожидаемый способ мог быть:
ClassTest.kt
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ClassTest : SuperTest() {
@Test
fun test() {}
private fun allowedWithoutAnnotation(paramAllowed: String) {}
}
build.gradle (: мобильный)
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
dependencies {
...
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.arch.core:core-testing:2.1.0'
}
GL
На самом деле среда тестирования дает слишком мало информации. Я столкнулся с той же проблемой, покопался в трассировке стека и нашел следующие подтверждения:
Итак, вы должны объявить свой@BeforeClass
метод static
.
Вам необходимо убедиться, что любой класс, отмеченный аннотацией @BeforeClass, является общедоступным статическим. Например:
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@RunWith(AndroidJUnit4.class)
public class EntityParcelTest {
@BeforeClass
public static void createEntities() {
// Setup...
}
@Test
public void someTest() {
// Testing here
}
Если после добавления следующие зависимости:
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation
'com.android.support.test.espresso:espresso-core:3.0.1'
а также
android{
defaultConfig{
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...
}
}
не помогает тогда использовать устаревший AndroidJUnit4
класс, который принадлежит к androidx.test.runner
пакет вместо того, который принадлежит androidx.test.ext.junit.runners.AndroidJUnit4
пакет.
Я до сих пор не понимаю, почему AndroidJUnit4
класс устарел, в то время как зависимости Gradle, к которым он относится, предложены командой Android повсеместно, т.е. Codelabs и docs.
В моем случае я пропустил тестовый пример аннотации с @Test
. Это не твой случай. Это ответ для таких, как я.
Ты можешь использовать @JvmField
, Из документации
Указывает компилятору Kotlin не генерировать геттеры / сеттеры для этого свойства и выставлять его как поле
@Rule
@JvmField
val activityActivityTestRule = ActivityScenarioRule<MainActivity>(MainActivity::class.java)
Еще одна проблема, с которой я столкнулся, начиная использовать эспрессо, — это ошибка
Failed to instantiate test runner class androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner site:stackoverflow.com
В этом случае я решил вернуть Unit в игру.
fun Example() : Unit {your code}
Пока-пока
Для меня проблема была @Rule
аннотаций..
Я пока не знаю причину.
В качестве временного решения проблемы вы можете, например, начать свою деятельность, используя UIAutomator для ActivityTestRule.
Я решил эту ошибку, просто установив удовольствие как личное, устранение этого решило это для меня.
Некоторые другие возможные причины:
- Имея только
@SmallTest
(или Средние / Большие-) методы испытаний. Маркировка хотя бы одного метода с помощью@Test
решает проблемы. setup()
/teardown()
методы не являются публичными.
Что касается моего случая, проблемой было определение параметра функции.
@Test
fun foo(string: String = "") { // Causes "Delegate runner could not be loaded" error.
// ...
}
Пришлось сделать что-то вроде следующего.
@Test
fun foo() {
foo("")
}
fun foo(string: String) { // Can be called from other test functions.
// ...
}
В моем случае я исправил это, явно объявив все @Test
, @Before
, а также @After
методы какpublic
, и объявив @BeforeClass
а также @AfterClass
как public static
Если вы оставите любой из этих методов неявно / явно protected
, или явно private
; вы столкнетесь со следующим исключением
java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded.
Итак, правильно использовать
@Before
public void setUp() {
}
@BeforeClass
public static void init() {
}
Вместо того:
@Before
void setUp() {
}
@Before
protected void setUp() {
}
@Before
private void setUp() {
}
@BeforeClass
public void init() {
}
Я исправил с этой конфигурацией:
Мои зависимости:
/*Instrumentation Test*/
androidTestImplementation "org.assertj:assertj-core:3.12.2"
androidTestImplementation ('androidx.test.espresso:espresso-core:3.2.0',{
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestImplementation "androidx.arch.core:core-testing:2.1.0-rc01"
В моем defaultConfig
раздел у меня есть:
defaultConfig {
applicationId "uy.edu.ude.archcomponents"
minSdkVersion 22
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
}
В тесте у меня есть:
package uy.edu.ude.archcomponents.repository
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.room.Room
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.example.android.roomwordssample.WordDao
import com.example.android.roomwordssample.WordRoomDatabase
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import uy.edu.ude.archcomponents.entity.Word
import java.io.IOException
@RunWith(AndroidJUnit4::class)
class WordDaoTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var wordDao: WordDao
private lateinit var db: WordRoomDatabase
@Before
fun createDb() {
val context = InstrumentationRegistry.getInstrumentation().context
// Using an in-memory database because the information stored here disappears when the
// process is killed.
db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java)
// Allowing main thread queries, just for testing.
.allowMainThreadQueries()
.build()
wordDao = db.wordDao()
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
@Throws(Exception::class)
fun insertAndGetWord() {
runBlocking {
val word = Word("word")
wordDao.insert(word)
val allWords = wordDao.getAlphabetizedWords().waitForValue()
assertThat(allWords[0].word).isEqualTo(word.word)
}
}
}
Я также использую плагин Android версии 3.4.2. Я взял конфиг отсюда
Потратив много времени на решение одной незначительной проблемы в тестировании пользовательского интерфейса Jetpack compose, я обнаружил, что проблема заключается в переменной composeTestRule, которая была частной, поэтому убедитесь, что ваша переменная composeTest не должна быть частной.
@get: Правило val composeTestRule = createAndroidComposeRule()
НЕ КАК
@get: закрытое правило val composeTestRule = createAndroidComposeRule()
Может быть, вы не обновили бегунок в файле конфигурации gradle?
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Кроме того, AndroidStudio 3.2 имеет возможность автоматизировать миграцию ваших зависимостей в AndroidX (Refactor -> Migrate to AndroidX...), который сделал это для меня.