Я переношу свое приложение на androidx, мне кажется, что мои модульные тесты не работают. Я взял пример из Google AndroidJunitRunnerSample, который был обновлен для использования нового api androidx. При попытке запустить тесты я получаю следующую ошибку:
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
}
Что я делаю неправильно?
Может быть, вы не обновили раннер в конфигурационном файле gradle?
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Кроме того, в AndroidStudio 3.2 есть возможность автоматизировать миграцию ваших зависимостей в AndroidX (Refactor -> Migrate to AndroidX ...), которая сделала это за меня.
Кажется, у меня уже есть этот testInstrumentationRunner, установленный в моем build.gradle, и я также выполнил рефакторинг «Migrate to AndroidX».
Удаление аннотаций @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()
Пояснение здесь.
Но теперь тест - это тест JUnit.
Для меня проблема заключалась в том, что я использую @Rule вместо аннотации @get:Rule для ActivityTestRule. Если вы используете Kotlin, это может вам помочь.
В Kotlin для меня было достаточно замены @Rule на @get:Rule. Работает с @Before.
Для меня решением было изменить метод, помеченный @BeforeClass, на метод static согласно stackoverflow.com/a/733042/2848676. Похоже, это тоже помогло решить исходную проблему.
Замена @Rule на @get:Rule у меня сработала в котлине. Спасибо :)
Спасибо, удаление @RunWith (AndroidJUnit4.class) сработало. Обычно мне нравятся объяснения, но, кажется, я их не нахожу. Согласно документации, аннотация обязательна и должна работать developer.android.com/training/testing/junit-runner Я предполагаю здесь, но это просто кажется несовместимостью некоторой библиотеки / класса. Надеюсь, кто-то, у кого есть время, сможет разобраться, я сделаю это, если найду. Но пока это решение работает, так что голосуйте.
Для меня проблема заключалась в аннотации @Rule..
Я пока не знаю причины.
В качестве временного обходного пути вы можете, например, начать свою деятельность с помощью UIAutomator для ActivityTestRule.
Вы можете использовать @JvmField. Из документации
Instructs the Kotlin compiler not to generate getters/setters for this property and expose it as a field
@Rule
@JvmField
val activityActivityTestRule = ActivityScenarioRule<MainActivity>(MainActivity::class.java)
Вы также получаете сообщение об ошибке, если используете тестовое правило в Kotlin и пишете его в стиле Java.
@Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java)
Вы должны сменить @Rule на @get:Rule
@get:Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java)
В чем разница?
с @get: вы добавляете эту аннотацию к подчиненному java-получателю, созданному для этого поля (который является методом) вместо самого поля
Хорошая точка зрения. Я предпочитаю использовать @JvmField вместо префикса аннотации @get:, поскольку он генерирует тот же байт-код, что и Java с первоначально упомянутым синтаксисом. Я не скажу, что это личное предпочтение (поскольку он делает разные вещи), но до сих пор он был защищен от ошибок в сотнях тестов, и я предпочитаю его, так как могу проверить, что он работает.
Если после добавления следующих зависимостей:
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 и документы
Сообщение об ошибке отображается, когда я удаляю метод @Test.
Можно попробовать поставить метод @Test и запустить
@Test
public void signInTest() {
}
Как ни странно, это было для меня. Как только я добавил пустую функцию с пометкой @Test, все было хорошо
Изменение
@Test
void someTest() {
// Testing here
}
к
@Test
public void someTest() {
// Testing here
}
работает для меня.
Я выпотрошил эту ошибку, потому что просто установил удовольствие как частное, удаление этого решило это для меня.
Я исправил эту конфигурацию:
Мои зависимости:
/*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. Конфиг взял от здесь
Некоторые другие возможные причины:
@SmallTest (или средние / большие). Маркировка хотя бы одного метода с помощью @Test решает проблемы.setup() / teardown() методы не являются общедоступными.Есть два способа решить эту проблему:
Сборка -> Чистый проект. Затем Build -> Rebuild Project
Есть 2 пакета с AndroidJUnit4
androidx.test.runner (не рекомендуется)androidx.test.ext.junit.runnersУбедитесь, что в вашем build.gradle (приложении) есть эта строка
android {
defaultConfig {
....
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
....
}
}
и убедитесь, что вы используете (1) (который устарел) вместо (2) (новый, но еще не стабильный) в @RunWith(AndroidJUnit4.class) перед тестовым классом.
У меня ушло около 2 часов, чтобы наконец настроить один-единственный AndroidJUnitTest, хотя и следуя официальным документам. Как указано в этом ответе в моем тестовом классе, я изменил импорт: USE: import androidx.test.runner.AndroidJUnit4 (хотя и не рекомендуется) НЕ ИСПОЛЬЗУЙТЕ: import androidx.test.ext.junit.runners.AndroidJUnit4. Не идеальное решение. Но лучше использовать устаревший класс вместо того, чтобы тратить больше времени на запуск одного теста. Спасибо за Ваш ответ.
Что касается моего случая, проблемой было определение параметра функции.
@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.
// ...
}
Во время тестирования активности убедитесь, что переменная ActivityTestRule является общедоступной:
@Rule
public ActivityTestRule<YourActivity> activityTestRule = new ActivityTestRule<>(YourActivity.class);
В моем случае я исправил это, явно объявив все методы @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() {
}
AndroidJUnit4 НЕ ПОДДЕРЖИВАЕТ в функциях с аннотациями @Test ни в классе, ни в суперклассе:
@Test fun dontWork(param: String) {}@Test private fun dontWork2() {}Примечание: без аннотаций @Test вышеуказанное разрешено.
Ожидаемый способ мог быть:
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) {}
}
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, является общедоступный статический. Например:
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
}
В моем случае я пропустил тестовый пример аннотирования с @Test. Это не ваш случай. Этот ответ предназначен для таких, как я.
Проведя много времени над одной незначительной проблемой в тестировании пользовательского интерфейса Jetpack compose, я обнаружил, что проблема заключается в переменной composeTestRule, которая была частной, поэтому Убедитесь, что ваша переменная composeTest не должна быть частной.
@get: Правило val composeTestRule = createAndroidComposeRule ()
НЕ КАК
@get: Правило частный val composeTestRule = createAndroidComposeRule ()
Пожалуйста, обратитесь к сообщению ниже stackoverflow.com/questions/55496047/…