Null pointer exception when stubbing - android

Good afternoon, I have a nullpointer exception when stubbing
package com.micheladrien.android.fresquerappel
import android.app.Application
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.micheladrien.fresquerappel.R
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnitRunner
#RunWith(MockitoJUnitRunner::class)
class MainViewModelTest {
#Rule
#JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule()
#Mock
val applicationMock: Application = mock(Application::class.java)
#Before
fun setUpTaskDetailViewModel() {
`when`(applicationMock.getString(R.string.collage_climat)).thenReturn("Climat")
}
}
edit : I need to stub the function when(applicationMock.getString(R.string.collage_climat)).thenReturn("Climat")
because my viewmodel will get strings from context.
According to this blog post : https://codepills.com/2018/05/10/3-basic-mistakes-for-nullpointerexception-when-mock/
I should replace when thenreturn by when then (answer) which if it's true, why ?
I have already Tested : Changing R.id value to a brut number. => Same error
Mocking the file inside the before and using lateinit for the declaration at #Mock => same error
Unlike previous question thread Mockito - NullpointerException when stubbing Method
I am directly stubbing the method. Not stubbing the method of the object of another method.
Any help would be greatly appreciated.
Edit : The VM I aim to test :
class MainViewModel(application: Application): AndroidViewModel(application), WaitingViewModel{
private val _name = MutableLiveData<String>().apply {
value = application.getString(R.string.collage_climat)
}
val name : LiveData<String> = _name
override fun notifyNewCollage(collage_name: String) {
_name.value = collage_name
}
}
Here is the gradle if you want to check the Mockito version :
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.3'
def navigation_version = '2.3.1'
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation "androidx.navigation:navigation-fragment:$navigation_version"
implementation "androidx.navigation:navigation-ui:$navigation_version"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
implementation 'il.co.theblitz:observablecollections:1.4.2'
def espressocore_version = '3.3.0'
androidTestImplementation "androidx.test.espresso:espresso-core:$espressocore_version"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressocore_version"
androidTestImplementation "android.arch.core:core-testing:$lifecycle_version"
def mockito_version = '3.5.5' // For local unit tests on your development machine (also tested on 3.3.3)
testImplementation "org.mockito:mockito-core:$mockito_version" // For instrumentation tests on Android devices and emulators
androidTestImplementation "org.mockito:mockito-android:$mockito_version"
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
}

Mockito advices against Mocking classes you don't own. So an Application mock is a bad idea.
Junit can give you the application context needed : https://developer.android.com/training/testing/junit-runner
For other info about stubbing fail, Mockito fails on stubbing : it tries to execute the function that should be stubbed

Related

Hilt Test Unit with View Model, No Instrumentation Registered

I want to implementation Dependecy Injection using Hilt to ViewModel. And that working. but i can't test it. it show error. I was read some other post, but no one is answered.
This error maybe too many test dependency and conflict.
Version
Hilt version = 2.35.1
Android Studio = 4.2.1
Test class
simplest code won't run
package com.unlink.moviecatalogue6.ui.movie
import com.unlink.moviecatalogue6.data.repository.MovieRepository
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import javax.inject.Inject
#HiltAndroidTest
class MovieViewModelTest {
#get:Rule var hiltrule = HiltAndroidRule(this)
#Inject
lateinit var repository: MovieRepository
#Before
fun setUp() {
hiltrule.inject()
}
#Test
fun some(){
//
}
}
Error
but error at #get:Rule
java.lang.IllegalStateException: No instrumentation registered! Must run under a registering instrumentation.
at androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(InstrumentationRegistry.java:45)
at androidx.test.core.app.ApplicationProvider.getApplicationContext(ApplicationProvider.java:41)
at dagger.hilt.android.internal.testing.MarkThatRulesRanRule.<init>(MarkThatRulesRanRule.java:41)
at dagger.hilt.android.testing.HiltAndroidRule.<init>(HiltAndroidRule.java:36)
at com.unlink.moviecatalogue6.ui.movie.MovieViewModelTest2.<init>(MovieViewModelTest2.kt:19)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:250)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:260)
at org.junit.runners.BlockJUnit4ClassRunner$2.runReflectiveCall(BlockJUnit4ClassRunner.java:309)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Setup Custom Test Runner
i try make custom test runner
package com.unlink.moviecatalogue6
class CustomTestRunnerByMe : AndroidJUnitRunner() {
override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
}
here my full build.gradle
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
buildFeatures{
viewBinding true
}
defaultConfig {
applicationId "com.unlink.moviecatalogue6"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
//custom test runner
testInstrumentationRunner "com.unlink.moviecatalogue6.CustomTestRunnerByMe"
//testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
// viewmodels
implementation 'androidx.activity:activity-ktx:1.2.3'
implementation 'androidx.fragment:fragment-ktx:1.3.3'
//image library
implementation 'de.hdodenhof:circleimageview:3.1.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.palette:palette-ktx:1.0.0'
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
//Kotlin Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_android_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_android_version"
//Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
kapt "androidx.hilt:hilt-compiler:1.0.0"
// default testing
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
// mockito testing
testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0"
//coroutine test
//For runBlockingTest, CoroutineDispatcher etc.
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2"
// hilt testing
// For Robolectric tests.
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
// For instrumented tests.
androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hilt_version"
// Espresso dependencies
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-intents:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-accessibility:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-web:$espresso_version"
androidTestImplementation "androidx.test.espresso.idling:idling-concurrent:$espresso_version"
}
kapt{
correctErrorTypes true
}
i was follow documentation at Hilt Documentation and Google Developer Document
Any response will very appreciate, Thanks for helping
Using RobolectricTestRunner ,we can run the test that depends on the android framework without emulator or real device.
ViewModel is a android library.So to test the ViewModel need to add RobolectricTestRunner.
For that first add dependency
//Unit test
testImplementation 'org.robolectric:robolectric:4.4'
//UI test
androidTestImplementation 'org.robolectric:robolectric:4.4'
Then Add these code above your test class.
#HiltAndroidTest
#RunWith(RobolectricTestRunner.class)
#Config(sdk = {Build.VERSION_CODES.VERSION_CODE}, application = HiltTestApplication.class)
public class HiltMainViewModelTest{
//your test code
}
Now your HiltCustomTestRunner will use RobolectricTestRunner to test android code.
package com.unlink.moviecatalogue6.ui.movie
import com.unlink.moviecatalogue6.data.repository.MovieRepository
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Before
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import javax.inject.Inject
#HiltAndroidTest
#RunWith(AndroidJUnit4::class)
class MovieViewModelTest {
#get:Rule var hiltrule = HiltAndroidRule(this)
#Inject
lateinit var repository: MovieRepository
#Before
fun setUp() {
hiltrule.inject()
}
#Test
fun some(){
//
}
}
Add #RunWith(AndroidJUnit4::class)

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

I am new to android testing and I donĀ“t know how to solve this problem.
Trying to run the following test file from my project:
#RunWith(AndroidJUnit4::class)
class AppDatabaseTest {
private lateinit var userDAO: UserDAO // custom DAO interface
private lateinit var db: AppDatabase
#Before
fun createDb() {
db = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
AppDatabase::class.java
).allowMainThreadQueries().build()
userDAO = db.userDAO()
}
#After
#Throws(IOException::class)
fun closeDb() {
db.close()
}
#Test
#Throws(Exception::class)
fun insertAndReadUser() {
val user = User(
1,
"123",
"Test Name",
"link.to/name/image#from_the.web"
)
userDAO.insert(user)
val inserted = userDAO.getByUID(1)
assertThat(inserted).isNotNull()
}
}
Throws the following error on the run log:
java.lang.RuntimeException: Delegate runner androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner for AndroidJUnit4 could not be found.
Caused by: java.lang.ClassNotFoundException: androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
Here are the project dependencies:
dependencies {
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
testImplementation 'com.google.truth:truth:1.1'
testImplementation 'junit:junit:4.13.1'
testImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
What could be causing this error and how to fix it?
Android Studio 4.1.1
I managed to find the issue.
In fact, I had put the testing file on the wrong folder, the [test] folder instead of [androidTest] folder. Moving the file to the correct folder managed to solve the issue.

Android FragmentScenario - Unable to find package androidx.fragment.app.testing

I'm getting this error while trying to use FragmentScenario in Android
error: package androidx.fragment.app.testing does not exist
import androidx.fragment.app.testing.FragmentScenario;
^
This is my simple Unit test example:
package com.example.myapplication;
import androidx.fragment.app.testing.FragmentScenario;
import android.os.Build;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
#RunWith(AndroidJUnit4.class)
#Config(manifest = Config.NONE, sdk = Build.VERSION_CODES.P)
public class MainActivityTest {
#Test
public void testFragmentScenario() {
FragmentScenario<BlankFragment> scenario = FragmentScenario.launchInContainer(BlankFragment.class);
}
}
build.gradle(app):
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
def test_version = '1.2.0'
def fragment_version = '1.2.4'
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
testImplementation "androidx.test:core:$test_version"
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation "androidx.test:runner:$test_version"
testImplementation "androidx.test:rules:$test_version"
testImplementation 'org.robolectric:robolectric:4.3'
}
What am I missing because a few months ago this piece of code works.
Check your build variants in Android Studio. It should be debug
It looks that you use the wrong way, try to change debugImplementation to testImplementation in build.gradle. Hope it will helpful to you.

Unresolved androidx classes for writing unit tests

I am trying to write my first test and I have problem figuring out the right dependencies to get everything to work. Here is my test class
class EmployeeDatabaseTest {
private lateinit var employeeDao: EmployeeDAO
#Before
fun setup() {
EmployeeDatabase.TEST_MODE = true
employeeDao = EmployeeDatabase.getDatabase(??).employeeDao()
}
#Test
fun should_Insert_Employee_Item() {
val employee = Employee("xx", "xx", 31, Gender.MALE)
employee.id = 1
runBlocking { employeeDao.addNewEmployee(employee) }
val employeeTest = runBlocking { getValue(employeeDao.getEmployeeById(employee.id!!)) }
Assert.assertEquals(employee.name, employeeTest.name)
}
}
Normally I would obtain context by InstrumentationRegistry.getContext()...but InstrumentationRegistry can't be resolved. It also can't resolve getValue(..) method. I am new to testing but I bet is something with dependencies. Here is my build.gradle:
dependencies {
...
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
}
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Do I miss something I am I doing something wrong?
A simple Context can be resolved with
androidx.test.core.app.ApplicationProvider.getApplicationContext()
which is part of the core module
androidTestImplementation 'androidx.test:core:1.2.0'
When you use Robolectric, you can also have it as
testImplementation 'androidx.test:core:1.2.0'

Error:Gradle: Execution failed for task ':app:transformDexArchiveWithDexMergerForDebug'

I build app using Kotlin with MVVM architecture, and now i write Unit Test for my ViewModel, just use JUnit and Mockito
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:design:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'
implementation 'com.android.support:cardview-v7:26.1.0'
implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.github.bumptech.glide:glide:4.1.0'
kapt 'com.github.bumptech.glide:compiler:4.1.0'
kapt 'com.android.databinding:compiler:3.0.1'
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'
testImplementation 'org.mockito:mockito-core:2.12.0'
}
But, when i try to run test, i got this following error :
Error:Gradle: Execution failed for task ':app:transformDexArchiveWithDexMergerForDebug'.
> com.android.build.api.transform.TransformException: com.android.dex.DexException: Multiple dex files define Landroid/support/compat/R$bool;
Here is my test class :
#RunWith(MockitoJUnitRunner::class)
class RegistrationFragmentViewModelTest {
var mViewModel: RegistrationFragmentViewModel? = null
#Mock var mContext: Context? = null
#Mock var mView: RegistrationView? = null
#Before
fun setUp() {
mViewModel = RegistrationFragmentViewModel(mContext!!, mView!!)
}
#Test
fun AfterInput_InvalidFirstName_ShowFirstNameError() {
// Given
val firstName = "fa"
val email = "fanjavaid#gmail.com"
val password = "demo"
// When
mViewModel?.doRegister()
// Then
verify(mViewModel)?.validateInput()
verify(mView)?.showFirstNameError(mContext?.resources?.getString(R.string.registration_error_email_format)!!)
}
}
I still don't know why. Any ideas?
Thank you?

Categories

Resources