Can you please give me steps how to setup project to be able to run unit test with hilt and mockito.
Here is my code of the test
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
class CustomDialogViewModelTest {
private val prefsManager : PrefsManager = mock()
private lateinit var viewModel : CustomDialogViewModel
#Before
fun setUp() {
viewModel = CustomDialogViewModel(prefsManager)
}
#Test
fun testFun(){
viewModel.getToDoItemFromPrefs()
}
}
And do I need to setup a separate module for tests?
I am currently trying to write an integration test for my repository layer that tests if I call a method, getExercises(), then it returns List<Exercise>, provided that the data is loaded into the local Firestore emulator ahead of time.
So far I got the local Firestore emulator to switch on and off at the beginning/end of a test run, respectively. I am able to populate my data into Firestore, and see the data in the local Firestore emulator via the web UI.
My problem is that my test assertion times out because the Task (an asynchronous construct the Firestore library uses), blocks the thread at the await() part in the repository method.
Test
package com.example.fitness.data
import androidx.test.ext.junit.runners.AndroidJUnit4
import app.cash.turbine.test
import com.example.fitness.Constants.EXERCISES_REF
import com.example.fitness.FirebaseEmulatorTest
import com.google.android.gms.tasks.Tasks
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
#HiltAndroidTest
#RunWith(AndroidJUnit4::class)
class ExerciseRepositoryTest : FirebaseEmulatorTest() {
#get:Rule
var hiltRule = HiltAndroidRule(this)
#Inject
lateinit var subject: ExerciseRepository
#Before
fun setup() {
hiltRule.inject()
}
#ExperimentalTime
#Test
fun `#getExercises returns a flow of exercises`() = runBlocking {
val exercises = mutableListOf<Exercise>().apply {
add(Exercise("a", "pushups"))
add(Exercise("b", "pull-ups"))
add(Exercise("c", "sit-ups"))
}
runBlocking(Dispatchers.IO) {
val task1 = firestoreInstance.collection(EXERCISES_REF).add(exercises.first())
val task2 = firestoreInstance.collection(EXERCISES_REF).add(exercises[1])
val task3 = firestoreInstance.collection(EXERCISES_REF).add(exercises.last())
Tasks.await(task1)
Tasks.await(task2)
Tasks.await(task3)
println("Done with tasks: task1: ${task1.isComplete}. task2: ${task2.isComplete}. task3: ${task3.isComplete}.")
}
println("About to get exercises")
subject.getExercises().test(timeout = Duration.seconds(5)) {
println("test body")
assertThat(awaitItem().size, `is`(4)) // Just checking that it passes for the right reasons first. This number should be 3
}
}
}
Repository (System under test)
package com.example.fitness.data
import com.example.fitness.Constants.EXERCISES_REF
import com.google.firebase.firestore.CollectionReference
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
#Singleton
class ExerciseRepository #Inject constructor(
#Named(EXERCISES_REF) private val exerciseCollRef: CollectionReference
) {
fun getExercises() = flow<List<Exercise>> {
println("beginning of searchForExercise")
val exercises = exerciseCollRef.limit(5).get().await() // NEVER FINISHES!!
println("Exercise count: ${exercises.documents}")
emit(exercises.toObjects(Exercise::class.java))
}
}
The output of this results in:
Done with tasks: task1: true. task2: true. task3: true.
About to search for exercises
beginning of searchForExercise
test body
Timed out waiting for 5000 ms
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 5000 ms
"Exercise count: 3" message never prints!
Note: I am using Robolectric 4.6.1, kotlinx-coroutines-playservices (1.5.0) to provide the await() extension function, and the Turbine testing library for flow assertions (0.6.1)
Perhaps of relevance is a superclass this test inherits that sets the main dispatcher to a test dispatcher.
package com.example.fitness
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.*
import org.junit.After
import org.junit.Before
import org.junit.Rule
abstract class CoroutineTest {
#Rule
#JvmField
val rule = InstantTaskExecutorRule()
protected val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
private val testCoroutineScope = TestCoroutineScope(testDispatcher)
#Before
fun setupViewModelScope() {
Dispatchers.setMain(testDispatcher)
}
#After
fun cleanupViewModelScope() {
Dispatchers.resetMain()
}
#After
fun cleanupCoroutines() {
testDispatcher.cleanupTestCoroutines()
testDispatcher.resumeDispatcher()
}
fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
testCoroutineScope.runBlockingTest(block)
}
Any help here would be greatly appreciate.
Edit
I have opened an issue with the kotlin extensions team to get more visibility on how to go about testing this, including a repo demonstrating the problem.
This problem has been resolved in a new version of the kotlinx-coroutines package (1.6.0-RC). See my github compare across branches. Tests now pass as expected with this version.
I am developing an android with Junit5 and Mockito.
Some tests are ParameterizedTest and others are just Test.
Here is my sample code.
When I run this test, only "ParameterizedTests" run.
"JustTests" is not shown on the JUnit test console list.
How can I run "JustTests" too?
import org.junit.Test
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.InOrder
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
internal class MyPresenterTest {
#Mock
private lateinit var view: MyContract.View
private lateinit var presenter: MyContract.Presenter
#BeforeEach
fun setup() {
MockitoAnnotations.openMocks(this)
presenter = MyPresenter(view)
}
#Nested
#DisplayName("Just Test")
inner class JustTests {
#DisplayName("test 1")
#Test
fun greetingTest1() {
...
}
}
#Nested
#DisplayName("Parameterized test")
inner class ParameterizedTests {
#ParameterizedTest(name = "{0}")
#ValueSource(strings = ["Hello", "Hi])
#Test
fun greetingTest2(greeting: String) {
...
}
}
}
I'm writing an instrumented test for an Android Room Database, but unfortunately it crashes due to the operation being done on the main thread:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. When configuring the test DB, I turned on the setting allowMainThreadQueries() to explicitly prevent this issue.
Sample code:
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.runner.AndroidJUnit4
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.IOException
import kotlin.test.assertTrue
#RunWith(AndroidJUnit4::class)
class NewsDatabaseTest {
private lateinit var cachedNewsDao: CachedNewsDao
private lateinit var db: NewsDatabase
#Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room.inMemoryDatabaseBuilder(context, NewsDatabase::class.java)
.allowMainThreadQueries()
.build()
cachedNewsDao = db.cachedNewsDao()
}
#After
#Throws(IOException::class)
fun closeDb() {
db.close()
}
#Test
fun ensure_dao_has_items() {
val newsList: List<CachedNews> = cachedNewsDao.getNews()
assertTrue { newsList.isEmpty() }
}
}
The stacktrace initially points to the empty line in the only test method. And you can see the first line is the line which I explicitly turned off in setUp.
at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:267)
at androidx.room.RoomDatabase.query(RoomDatabase.java:323)
at androidx.room.util.DBUtil.query(DBUtil.java:83)
at com.package.feature.news.cache.CachedNewsDao_Impl.getNews(CachedNewsDao_Impl.java:97)
at com.package.feature.news.cache.NewsDatabaseTest.ensure_dao_has_items(NewsDatabaseTest.kt:44)
Why is Room ignoring the allow main thread query config?
I found a workaround to run tests via command line: ./gradlew connectedAndroidTest.
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?
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.
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)
The error message displayed when I delete the #Test method.
You can try to put a #Test method and run
#Test
public void signInTest() {
}
While testing for activity make sure ActivityTestRule variable is public :
#Rule
public ActivityTestRule<YourActivity> activityTestRule = new ActivityTestRule<>(YourActivity.class);
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
androidx.test.runner (deprecated)
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.
Changing
#Test
void someTest() {
// Testing here
}
to
#Test
public void someTest() {
// Testing here
}
works for me.
The testing framework actually give too little information. I encountered the same issue and dug into the stack trace, and found these validation:
So you must declare your #BeforeClass method static.
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
}
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)
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
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
In my case, I skipped annotate test case with #Test. It is not your case. This answer is for others like me.
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.
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.
I gut this error for simply set fun as private, removing this solved this for me.
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
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.
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.
// ...
}
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() {
}
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()
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