Перенос тестов Junit4 на androidx: почему «делегатный бегун не может быть загружен»?

Я переношу свое приложение на androidx, но, похоже, мои модульные тесты не работают. Я взял пример из Google AndroidJunitRunnerSample, который был обновлен до используйте новый androidx 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
    }

Что я делаю не так?


person J. Nuutinen    schedule 18.10.2018    source источник
comment
Пожалуйста, обратитесь к сообщению ниже junit4 androidjunit4classrunner"> stackoverflow.com/questions/55496047/   -  person Tarun A    schedule 23.10.2020


Ответы (19)


Удаление аннотаций @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()

Объяснение здесь.

person J. Nuutinen    schedule 19.10.2018
comment
Но теперь тест — это тест JUnit. - person Ioane Sharvadze; 22.10.2018
comment
Для меня проблема заключалась в том, что я использую аннотацию @Rule вместо @get:Rule для ActivityTestRule. Если вы используете Kotlin, это может вам помочь. - person Ioane Sharvadze; 22.10.2018
comment
В Котлине мне было достаточно замены @Rule на @get:Rule. Работает с @Before. - person Miloš Černilovský; 13.12.2018
comment
Для меня решение состояло в том, чтобы изменить метод, аннотированный с помощью @BeforeClass, на метод static в соответствии с stackoverflow.com/a/733042/2848676. Похоже, что это, возможно, помогло и здесь с исходной проблемой. - person Michael Osofsky; 08.03.2019
comment
Замена @Rule на @get:Rule сработала для меня в Котлине. Спасибо :) - person Mohit Atray; 16.02.2020
comment
Спасибо, удаление @RunWith(AndroidJUnit4.class) сработало. Обычно мне нравится объяснение, но, похоже, я его не нахожу. В соответствии с документацией аннотация обязательна и должна работать developer.android.com/training/testing /junit-runner Я размышляю здесь, но, похоже, это какая-то несовместимость библиотеки/класса. Надеюсь, кто-то, у кого есть время, может исследовать, я сделаю это, если найду. Но пока это решение работает, так что проголосуйте за него. - person MG Developer; 11.10.2020

Вы также получите сообщение об ошибке, если используете тестовое правило в Kotlin и пишете его в стиле Java.

@Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java)

Вы должны изменить @Rule на @get:Rule

@get:Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java) 
person luckyhandler    schedule 01.03.2019
comment
В чем разница? - person IgorGanapolsky; 12.03.2019
comment
с @get: вы добавляете эту аннотацию к подчиненному java-геттеру, созданному для этого поля (которое является методом), а не к самому полю - person Filipkowicz; 10.04.2019
comment
Хорошая точка зрения. Я предпочитаю использовать префикс аннотации @JvmField вместо @get:, поскольку он генерирует тот же байт-код, что и Java с первоначально упомянутым синтаксисом. Я не скажу, что это личное предпочтение (поскольку он делает разные вещи), но до сих пор он был безошибочным в сотнях тестов, и я предпочитаю его, поскольку могу убедиться, что он работает. - person milosmns; 27.09.2019

Сообщение об ошибке, отображаемое при удалении метода @Test.

Вы можете попробовать поставить метод @Test и запустить

@Test
public void signInTest() {


}
person JackWu    schedule 24.05.2019
comment
Как ни странно, это случилось со мной. Как только я добавил пустую функцию, отмеченную @Test, все было хорошо - person Ivan Wooll; 10.10.2019

При проверке активности убедитесь, что переменная ActivityTestRule общедоступна:

@Rule
public ActivityTestRule<YourActivity> activityTestRule = new ActivityTestRule<>(YourActivity.class); 
person avinash    schedule 05.12.2019

Есть 2 способа, которыми вы можете попытаться решить эту проблему:

Решение 1:

Сборка --> Очистить проект. Затем Build -> Rebuild Project

Решение 2:

Есть 2 пакета с AndroidJUnit4

  1. androidx.test.runner (устарело)
  2. androidx.test.ext.junit.runners

Убедитесь, что в вашем build.gradle (приложении) есть эта строка

android {
    defaultConfig {
    ....
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    ....
    }
}

и убедитесь, что вы используете (1) (который устарел) вместо (2) (новый, но еще не стабильный) в @RunWith(AndroidJUnit4.class) перед вашим тестовым классом.

person Dung Nguyen    schedule 17.10.2019
comment
Мне потребовалось около 2 часов, чтобы наконец настроить один единственный AndroidJUnitTest, хотя и следовал официальной документации. Как указано в этом ответе, в моем тестовом классе я изменил импорт: ИСПОЛЬЗОВАТЬ: import androidx.test.runner.AndroidJUnit4 (хотя и устарело) НЕ ИСПОЛЬЗОВАТЬ: import androidx.test.ext.junit.runners.AndroidJUnit4. Не идеальное решение. Но лучше использовать устаревший класс вместо того, чтобы тратить больше времени на запуск одного теста. Спасибо за Ваш ответ. - person user2350644; 03.07.2020

Изменение

@Test
void someTest() {
    // Testing here
}

to

@Test
public void someTest() {
    // Testing here
}

работает на меня.

person zwlxt    schedule 21.06.2019

Фреймворк тестирования на самом деле дает слишком мало информации. Я столкнулся с той же проблемой, покопался в трассировке стека и нашел эти проверки:

введите здесь описание изображения

Поэтому вы должны объявить свой @BeforeClass метод static.

person Perqin    schedule 19.09.2019

Вы должны убедиться, что любой класс, помеченный аннотацией @BeforeClass, является public static . Например:

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
    }
person rayalois22    schedule 21.06.2020

Вы можете использовать @JvmField. Из документации

Указывает компилятору Kotlin не генерировать геттеры/сеттеры для этого свойства и выставлять его как поле

@Rule
@JvmField
val activityActivityTestRule = ActivityScenarioRule<MainActivity>(MainActivity::class.java)
person Aleksandr Urzhumtcev    schedule 25.01.2019

Если после добавления следующих зависимостей:

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 и документы

person Neeraj Sewani    schedule 15.05.2019

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

person Braian Coronel    schedule 21.05.2020

В моем случае я пропустил тестовый пример аннотации с @Test. Это не ваш случай. Этот ответ для таких, как я.

person MJ Studio    schedule 13.07.2020

Может быть, вы не обновили бегун в файле конфигурации Gradle?

defaultConfig {
    ...
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Кроме того, в AndroidStudio 3.2 есть возможность автоматизировать миграцию ваших зависимостей в AndroidX (Refactor -> Migrate to AndroidX...), которая сделала это для меня.

person davisjp    schedule 19.10.2018
comment
Кажется, у меня уже есть этот testInstrumentationRunner, установленный в моем build.gradle, и я также выполнил рефакторинг «Миграция на AndroidX». - person J. Nuutinen; 19.10.2018

Для меня проблемой была аннотация @Rule..
Я пока не знаю причину.

В качестве временного обходного пути вы можете, например, начать свою деятельность, используя UIAutomator для ActivityTestRule.

person Alessandro Biessek    schedule 22.10.2018

Я выпотрошил эту ошибку, просто установив удовольствие как приватное, удаление этого решило это для меня.

person Jesus Dimrix    schedule 30.06.2019

Я исправил с этой конфигурацией:

Мои зависимости:

/*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. Я взял конфигурацию из здесь

person JuanMoreno    schedule 19.08.2019

Некоторые другие возможные причины:

  • Наличие только @SmallTest (или средних/больших) методов тестирования. Пометка хотя бы одного метода @Test решает проблемы.
  • setup() / teardown() методы не являются общедоступными.
person WindRider    schedule 19.08.2019

Что касается моего случая, то определение параметра функции было проблемой.

@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.
    // ...
}
person solamour    schedule 25.10.2019

В моем случае я исправил это, явно объявив все методы @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() {

}
person Zain    schedule 04.03.2020