Миграция тестов Junit4 на androidx: что заставляет "делегат делегата не загружаться"?

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

Что я делаю неправильно?

Ответы

Ответ 1

Удаление @RunWith(AndroidJUnit4.class) из тестовых классов @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 аннотацию @RunWith, она работает. Также, если я @BeforeClass настройку @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()

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

Ответ 2

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

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

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

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

Ответ 3

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

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

@Test
public void signInTest() {


}

Ответ 4

изменения

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

в

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

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

Ответ 5

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

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.

Ответ 6

Есть 2 способа решить эту проблему:

Решение 1:

Построить → Чистый проект. Затем Build → Перестроить проект

Решение 2:

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

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

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

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

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

Ответ 7

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

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

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

Ответ 8

Для меня проблема заключалась в аннотации @Rule.
На данный момент я не знаю причины.

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

Ответ 9

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

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

@Rule
@JvmField
val activityActivityTestRule = ActivityScenarioRule<MainActivity>(MainActivity::class.java)

Ответ 10

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

Ответ 11

Я исправил с помощью этой конфигурации:

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

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

Ответ 12

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

  • Наличие только @SmallTest (или Medium-/Large-) методов испытаний. Маркировка хотя бы одного метода с помощью @Test решает проблемы.
  • Методы setup()/teardown() не являются публичными.

Ответ 13

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

enter image description here

Итак, вы должны объявить свой метод @BeforeClass static.