61

I am migrating my app to androidx, I can't seem to get my unit tests working. I took example from Google's AndroidJunitRunnerSample, which has been updated to use the new androidx api. I get the following error when trying to run my tests:

java.lang.Exception: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded. Check your build configuration.

Here is my module 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'
}

And here is how my tests are structured:

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
    }

What am I doing wrong?

Zoe
  • 27,060
  • 21
  • 118
  • 148
J. Nuutinen
  • 1,389
  • 1
  • 7
  • 12
  • Please refer below post https://stackoverflow.com/questions/55496047/delegate-runner-androidx-test-internal-runner-junit4-androidjunit4classrunner/64478059#64478059 – Tarun Anchala Oct 23 '20 at 04:21

21 Answers21

64

Removing @RunWith(AndroidJUnit4.class) annotations from the test classes fixed the issue, although I can't really say why or how it fixed it.

Edit: Allright I did some more testing. I migrated my app to Kotlin, and suddenly I noticed the tests began to work with the @RunWith annotation, too. Here's what I found out:

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....
    }
}

This java test fails with the Delegate runner for AndroidJunit4 could not be loaded error. But If I remove the @RunWith annotation, it works. Also, if I replace the @BeforeClass setup with just a @Before, like this:

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....
    }
}

The tests will run without errors. I needed to use the @BeforeClass annotation, so I just removed @RunWith.

But now that I am using Kotlin, the following (which should be equal to the first java example) works:

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...
    }

}

Also, as Alessandro Biessek said in an answer and @Ioane Sharvadze in the comments, the same error can happen with the @Rule annotation. If I add a line

 @Rule val instantTaskExecutorRule = InstantTaskExecutorRule()

To the Kotlin example, the same delegate runner error happens. This must be replaced with

@get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule()

Explanation here.

J. Nuutinen
  • 1,389
  • 1
  • 7
  • 12
  • But now, test is JUnit test. – Ioane Sharvadze Oct 22 '18 at 09:25
  • 13
    For me the problem was that I'm using `@Rule` instead of `@get:Rule` annotation for `ActivityTestRule`. If you are using `Kotlin` then it might help you. – Ioane Sharvadze Oct 22 '18 at 09:43
  • In Kotlin for me replacing `@Rule` by `@get:Rule` was sufficient. It works with `@Before`. – Miloš Černilovský Dec 13 '18 at 15:51
  • 1
    For me the solution was to change the method annotated with `@BeforeClass` to a `static` method as per https://stackoverflow.com/a/733042/2848676. Seems like that may have helped with the original problem here as well. – Michael Osofsky Mar 08 '19 at 00:00
  • 1
    Replacing ```@Rule``` with ```@get:Rule``` worked for me in kotlin. Thanks :) – Mohit Atray Feb 16 '20 at 19:16
  • Thanks, removing the @RunWith(AndroidJUnit4.class) worked. I usually like an explanation but do not seem to find one. Accoding to the documentation the annotation is required and should work https://developer.android.com/training/testing/junit-runner I am speculating here but it just seems to be its some library/class incompatibility. Hopefully someone who has time can investigate, I will do if I find some. But for now this solution works, so up voting it. – MG Developer Oct 11 '20 at 01:05
52

You also get the error message if you use a test rule in Kotlin and write it Java style

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

You have to change @Rule to @get:Rule

@get:Rule
var mainActivityActivityTestRule = ActivityTestRule(MainActivity::class.java) 
luckyhandler
  • 10,651
  • 3
  • 47
  • 64
  • 1
    What is the difference? – IgorGanapolsky Mar 12 '19 at 16:02
  • 3
    with @get: you are adding this annotation to underling java getter created for this field (which is method) instead of field itself – Filipkowicz Apr 10 '19 at 12:59
  • 2
    Good point. I prefer to use `@JvmField` instead of `@get:` annotation prefix as it generates the same bytecode that Java would with the originally mentioned syntax. I won't say it's a personal preference (as it does different things), but it's been error-proof in hundreds of tests so far and I do prefer it since I can verify that it's working. – milosmns Sep 26 '19 at 21:39
11

The error message displayed when I delete the @Test method.

You can try to put a @Test method and run

@Test
public void signInTest() {


}
JackWu
  • 1,036
  • 1
  • 12
  • 22
7

While testing for activity make sure ActivityTestRule variable is public :

@Rule
public ActivityTestRule<YourActivity> activityTestRule = new ActivityTestRule<>(YourActivity.class); 
avinash
  • 1,744
  • 24
  • 40
5

There are 2 ways that you can try to resolve this problem:

Solution 1:

Build --> Clean Project. Then Build --> Rebuild Project

Solution 2:

There are 2 packages that has of AndroidJUnit4

  1. androidx.test.runner (deprecated)
  2. androidx.test.ext.junit.runners

Make sure your build.gradle (app) has this line

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

and make sure you use (1) (which is deprecated) instead of (2) (new but not stable yet) in @RunWith(AndroidJUnit4.class) before your test class.

Nguyen Tan Dat
  • 3,780
  • 1
  • 23
  • 24
Dung Nguyen
  • 91
  • 2
  • 5
  • 2
    Took me around 2 hours to finally setup one single AndroidJUnitTest although following the official docs. As stated in this answer in my test class i changed the import: USE: `import androidx.test.runner.AndroidJUnit4` (although deprecated) DON'T USE: `import androidx.test.ext.junit.runners.AndroidJUnit4`. Not the perfect solution. But better to use a deprecated class instead of wasting more time just to get a single test running. Thanks for your answer. – ice_chrysler Jul 03 '20 at 10:45
2

Changing

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

to

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

works for me.

zwlxt
  • 301
  • 2
  • 9
2

The testing framework actually give too little information. I encountered the same issue and dug into the stack trace, and found these validation:

enter image description here

So you must declare your @BeforeClass method static.

Perqin
  • 755
  • 10
  • 27
2

AndroidJUnit4 DOESN'T SUPPORT in the functions with @Test annotations neither in the class nor in the superclass:

  • the parameters: @Test fun dontWork(param: String) {}
  • private access modifiers: @Test private fun dontWork2() {}

Note: Without @Test annotations the above is allowed


An expected way could be:

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 (:mobile)

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

Braian Coronel
  • 22,105
  • 4
  • 57
  • 62
2

You need to make sure that any class marked with the @BeforeClass annotation is public static . For example:

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
    }
rayalois22
  • 161
  • 3
  • 7
1

You can use @JvmField. From documentation

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)
1

If after adding the following dependencies:

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'

and

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

doesn't help then use the deprecated AndroidJUnit4 class which belongs to the androidx.test.runner package instead of the one belongs to androidx.test.ext.junit.runners.AndroidJUnit4 package.

I still don't understand why AndroidJUnit4 class is deprecated while the Gradle dependencies it belongs to is suggested by the Android team everywhere i.e Codelabs and docs

Neeraj Sewani
  • 3,952
  • 6
  • 38
  • 55
1

In my case, I skipped annotate test case with @Test. It is not your case. This answer is for others like me.

MJ Studio
  • 3,947
  • 1
  • 26
  • 37
0

Maybe you haven't updated the runner on the gradle config file?

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

Also, AndroidStudio 3.2 has an option to automate the migration of your dependencies to AndroidX (Refactor -> Migrate to AndroidX...) that did that for me.

davisjp
  • 730
  • 1
  • 11
  • 24
  • Seems I already have that testInstrumentationRunner set in my build.gradle, and I've also done the 'Migrate to AndroidX' refactor. – J. Nuutinen Oct 19 '18 at 16:57
0

For me the problem was the @Rule annotation..
I don't know the cause for now.

As a temporary workaround yo can for example start your activity using UIAutomator for ActivityTestRule.

0

I gut this error for simply set fun as private, removing this solved this for me.

Jesus Dimrix
  • 4,378
  • 4
  • 28
  • 62
0

I fixed with this configuration:

My dependencies:

/*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"

In my defaultConfig section I have:

defaultConfig {
    applicationId "uy.edu.ude.archcomponents"
    minSdkVersion 22
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

    testInstrumentationRunnerArguments clearPackageData: 'true'
}

In the test I have:

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)
        }
    }
}

I also use the android plugin version 3.4.2. I took the config from here

JuanMoreno
  • 2,498
  • 1
  • 25
  • 34
0

Some other possible reasons:

  • Having only @SmallTest (or Medium-/Large-) test methods. Marking at least one method with @Test solves the issues.
  • setup() / teardown() methods are not public.
WindRider
  • 11,958
  • 6
  • 50
  • 57
0

As for my case, defining a function parameter was the problem.

@Test
fun foo(string: String = "") {  // Causes "Delegate runner could not be loaded" error.
    // ...
}

I had to do something like the following.

@Test
fun foo() {
    foo("")
}

fun foo(string: String) {  // Can be called from other test functions.
    // ...
}
solamour
  • 2,764
  • 22
  • 22
0

In my case, I fixed it by explicitly declaring all @Test, @Before, and @After methods aspublic, and declaring @BeforeClass and @AfterClass as public static

If you leave any of those methods as implicitly/explicitly protected, or explicitly private; you'll encounter the following exception

java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded.

So, the correct thing is to use

@Before
public void setUp() {

}

@BeforeClass
public static void init() {

}

Instead of:

@Before
void setUp() {

}

@Before
protected void setUp() {

}

@Before
private void setUp() {

}

@BeforeClass
public void init() {

}
Zain
  • 37,492
  • 7
  • 60
  • 84
0

After spending lots of time in one minor problem in jetpack compose UI testing I found that the issue is in composeTestRule variable that was it is private so Make sure that your composeTest variable should not be private.

@get:Rule val composeTestRule = createAndroidComposeRule()

NOT LIKE

@get:Rule private val composeTestRule = createAndroidComposeRule()

0

Another issue I was facing starting to using Espresso is the error

Failed to instantiate test runner class androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner site:stackoverflow.com

In this case I solved returning Unit to the fun

 fun Example() : Unit {your code}

Bye bye

Dharman
  • 30,962
  • 25
  • 85
  • 135
Lucas
  • 458
  • 4
  • 6