Dagger2 issue in Android Studio - android

I'm writing some JUnits for my Android app which use Dagger2. To demonstrate my problem I simplified my test.
In gradle I have those dependencies related to Junits and dagger:
//dependency injection
api "com.google.dagger:dagger:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
api "com.google.dagger:dagger-android:$dagger_version"
api "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
//unit testing
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//dagger testing
testImplementation "com.google.dagger:dagger:$dagger_version"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
Here is my Test class
#RunWith(MockitoJUnitRunner.class)
public class Test extends TestCase {
#Inject
String greating;
#Before
public void setUp() {
TestComponent component = DaggerTestComponent.builder().testModule(new TestModule()).build();
component.inject(this);
}
#org.junit.Test
public void test() {
assertEquals("Hello!", greating);
}
}
Component for DI
#Singleton
#Component(modules = {TestModule.class})
public interface TestComponent {
void inject(Test test);
}
Test module for DI
#Module
public class TestModule {
#Singleton
#Provides
public String providesHello() {
return "Hello!";
}
}
My problem is that during the execution of the test, DaggerTestComponent is generated, but Android Studio underlines with red color the new TestModule() on line where is TestComponent component = DaggerTestComponent.builder().testModule(new TestModule()).build(); On hower it shows me testModule(TestModule) in builder cannot be aplied to mytestpackage.TestModule. If I open generated DaggerTestComponent it look ok but contains error cannot resolve symbol TestModule on it's every occurence.
DaggerTestComponent is generated under
\app\build\generated\ap_generated_sources\debugUnitTest\out\mytestpackage
My test and related classes are located in
\app\src\test\java\mytestpackage
Regardless this, the Test works fine and ends as passed.
How do I get rid off this annoying error? Do I need to configure additional classpaths in gradle or set something else in AndroidStudio? Thanks in advance!

Finally problem solved by adding
android.sourceSets {
test.java.srcDirs +=
[file("$buildDir/generated/ap_generated_sources/debugUnitTest/out")]
}
Thanks to Daivid for solution - https://stackoverflow.com/a/61329262/4345570

Related

Dagger dependencies not getting in AndroidTest package

I am trying to get dagger component dependency in my AndroidTest package
Dependencies
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
debugImplementation 'androidx.test:runner:1.5.1'
debugImplementation 'androidx.test:rules:1.5.0'
debugImplementation "androidx.test:core-ktx:1.5.0"
kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
Code in AndroidTest package
Component
#Singleton
#Component
interface TestUserComponent{
fun inject(myActivityTest: MyActivityTest)
}
Test file
class MyActivityTest {
lateinit var mTestUserComponent: TestUserComponent
#Before
fun setUp() {
mTestUserComponent = DaggerTestUserComponent.builder().build()
}
}
From above code I am not getting autogenerated file "DaggerTestUserComponent"

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.

Null pointer exception when stubbing

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

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'

Libraries included in custom library not usable/visible in sample project Android

i'm creating simple library which make call to weather api, in my lib-project i use RxJava and RxAndroid and retrofit for http calls.
My WeatherService make the call and receive result as json and need to make some manipulations and return it as Single<CurrentWeather> to the client app.
In my lib-project i add to gradle all needed dependencies for rx and retrofit.
My library gradle:
ext{
rxJava = "2.1.17"
rxAndroid = "2.0.2"
support = "27.1.1"
retrofitVersion = "2.4.0"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.support:appcompat-v7:$support"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroid"
implementation "io.reactivex.rxjava2:rxjava:$rxJava"
}
Now in my sample app in MainActivity i wanted to test one function to see if it works as expected, but when i try call the function which return Single it show comlpile error:
error: cannot access Single
class file for io.reactivex.Single not found
lib function declaration:
public Single<Current> getCurrentWeather(double lat, double lng) {
return mClient.getWeather(lat + "," + lng).flatMap(new Function<JSONObject, SingleSource<Current>>() {
#Override
public SingleSource<Current> apply(JSONObject jsonObject) throws Exception {
//parse jsonObject and return as Single<Current>
return Single.create(new SingleOnSubscribe<Current>() {
#Override
public void subscribe(SingleEmitter<Current> emitter) throws Exception {
Current current = new Current();
emitter.onSuccess(current);
}
});
}
});
}
Client app MainActivity: Here i have the compile error.
WeatherService.getIsnstance().getCurrentWeather(32.5554, 35.545)
Client app gradle:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation project(":weatherlib")
}
Client app settings.gradle:
include ':app', ':weatherlib'
Do i miss something here ?
Do this means that everyone who will wish to use this lib will have to add rx dependencies in their gradle too?
You need to use api instead of implementation for your library's dependencies.
See here for the difference between them.

Categories

Resources