I have a separate Android library in a separate project for an Android app. However I link the library directly (referencing the library module directly by path or the generated AAR) I get the following error:
A problem was found with the configuration of task ':app:kaptDevelopDebugKotlin'.
> Cannot write to file '/home/m0skit0/Repos/repo/app-android/app/build/intermediates/data-binding/develop/debug/bundle-bin' specified for property 'dataBindingArtifactOutputDir' as it is a directory.
e: error: cannot access ActivityDeparturesBinding
class file for com.blablabla.databinding.ActivityDeparturesBinding not found
Consult the following stack trace for details.
com.sun.tools.javac.code.Symbol$CompletionFailure: class file for com.blablabla.databinding.ActivityDeparturesBinding not found
* What went wrong:
Execution failed for task ':app:kaptDevelopDebugKotlin'.
This shows just after the task :app:transformDataBindingWithDataBindingMergeArtifactsForDevelopDebug
However the class does exist, with full canonical name. It belongs to the the library and it is correctly auto generated (without any errors) by Android's Data Binding processor on both projects. The library compiles correctly on its own. There's no further stacktrace even if the compilation is run with --stacktrace.
I have tried linking the library with compile, implementation and api, all with the same result.
Gradle version is 4.4.
UPDATE:
Updating to Gradle 5 did not solve the problem. Renaming the XML did not solve the problem.
I've also found that the error happens only when I reference the ActivityDeparturesBinding class in my code, even if the part where it is referenced is never actually called. Only importing it without referencing it does not cause the error.
UPDATE2:
This is the activity that uses the layout:
class DeparturesActivity : BaseVinPlateActivity<DeparturesViewModel, ActivityDeparturesBinding>() {
companion object {
fun getStartIntent(context: Context) = Intent(context, DeparturesActivity::class.java)
}
#Inject
override lateinit var viewModel: DeparturesViewModel
override val layout: Int = R.layout.activity_departures
override fun injectThis(component: ActivityComponent) {
component.inject(this)
}
override fun getToolbarTitleId(): Int = R.string.departures_title
override fun initializeDataBindings(dataBinding: ActivityDeparturesBinding) {
dataBinding.viewmodel = viewModel
}
}
abstract class BaseVinPlateActivity<T: BaseVinPlateViewModel, U: ViewDataBinding> : CompoundsBaseActivity() {
protected abstract var viewModel: T
protected abstract val layout: Int
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == BARCODE_RESULT_CODE_ACTIVITY && resultCode == Activity.RESULT_OK) {
val barcodeResult = data?.getStringExtra(BARCODE_RESULT_ACTIVITY)
viewModel.setBarcodeResult(barcodeResult)
}
}
override fun initializeView() {
super.initializeView()
val dataBinding = inflateDataBinding<U>(layout)
initializeDataBindings(dataBinding)
with (viewModel) {
setBindValidator(Validator(dataBinding))
loadData()
}
}
protected abstract fun initializeDataBindings(dataBinding: U)
}
If I remove the initializeDataBindings() function, the error is gone. Note that commenting only the body of the function is not enough, I had to remove the whole function.
This smells like a compiler/tools bug.
I know it's late but I'd like to say that something similar happened to me, in my case I have a library module which was using a third party library, and then in my app module I'm using dagger for injecting dependencies, and I was getting this issue because, I believe dagger couldn't find this thrid party library's class because it wasn't directly implemented in the app's dependencies. So in my case what got it sorted was to change the way I was adding the third party library into my local library module in order to use api instead of implementation.
We could bypass the compilation error in a couple of ways (there are probably more ways). The best way we came up with is converting the abstract function into an abstract lambda property.
Parent class:
abstract class BaseVinPlateActivity<T: BaseVinPlateViewModel, U: ViewDataBinding> : CompoundsBaseActivity() {
protected abstract val initializeDataBinding: U.() -> Unit
// Rest of code
}
Child class:
class DeparturesActivity : BaseVinPlateActivity<DeparturesViewModel, ActivityDeparturesBinding>() {
override val initializeDataBinding: ActivityDeparturesBinding.() -> Unit = { viewmodel = this#DeparturesActivity.viewModel }
// Rest of code
}
Related
I have a custom Logger used thorough the application. It is doing more, but it can be simplified as:
class MyLogger {
companion object {
fun d(tag: String, msg: String): Int {
return Log.d(tag, msg)
}
}
}
Again, for simplicity, asume a function like the following:
fun addWithLogger(val1: Int, val2: Int): Int {
MyLogger.d("TEST", "adding $val2 to $val1")
return (val1 + val2).also {
MyLogger.d("TEST", "result $it")
}
}
I am trying to test the function using JUnit, and mock the MyLogger.d function using mockk library:
#Test
fun additionWithLogger_isCorrect() {
val testClass = TestClass()
mockkStatic(MyLogger::class)
every { MyLogger.d(any(), any()) } returns 0
assertEquals(5, testClass.addWithLogger(2, 3))
}
I have tried with mockkStatic, mockkObject, with MyLogger.Companion etc, but all my attempts are resulting in something simillar:
Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
java.lang.RuntimeException: Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.util.Log.d(Log.java)
at example.MyLogger$Companion.d(MyLogger.kt:8)
at example.ExampleUnitTest$additionWithLogger_isCorrect$1.invoke(ExampleUnitTest.kt:25)
at example.ExampleUnitTest$additionWithLogger_isCorrect$1.invoke(ExampleUnitTest.kt:25)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
at io.mockk.MockKDsl.internalEvery(API.kt:94)
at io.mockk.MockKKt.every(MockK.kt:143)
at example.ExampleUnitTest.additionWithLogger_isCorrect(ExampleUnitTest.kt:25)
...
It seems, that the MyLogger.d function is still being invoked, despite I am trying to mock it? What am I missing?
I have tried mockito and mockk libraries, but with no luck. I would like to test the functionality using Unit tests on PC. Tests are running ok as instrumental on Android device.
Code to be tested is already written, so I do not have much possibilities to adjust the MyLogger inside...
I created a validation use case in which I'm validating the input using isDigitsOnly that use TextUtils internally.
override fun isDigitsOnly(size: String): Boolean {
return !size.trim().isDigitsOnly()
}
when I tried to test it, I got this error
Method isDigitsOnly in android.text.TextUtils not mocked
Does anyone know how I can mock the textUtils in my test class
#RunWith(MockitoJUnitRunner::class)
class ValidationInputImplTest {
#Mock
private lateinit var mMockTextUtils: TextUtils
private lateinit var validationInputImpl: ValidationInputImpl
#Before
fun setUp() {
validationInputImpl = ValidationInputImpl()
}
#Test
fun `contains only digits, returns success`() {
val input = "66"
val result = validationInputImpl(input)
assertTrue(result is ValidationResult.Success)
}
}
At the time of writing the answer,you cannot do that. Because the android code ,that you see is not actual code. You cannot create unit test for that. The default implementation of the methods in android.jar is to throw exception.
One thing you can do is, adding the below in build.gradle file.
But it make the android classes not to throw exception. But it will always return default value. So the test result may actually not work. It is strictly not recommended.
android {
testOptions {
unitTests.returnDefaultValues = true
}
}
The better way to do copy the code from android source and paste the file under src/test/java folder with package name as android.text .
Link to Answer
I am facing the same issue as asked in the below question. please help me out.
Mock a "global" property in Kotlin
I tried solution provided in above question but nothing is working. and I am asking the same question because I am not able to post any comment on the previous question.
I am trying to write test case for below class
class CustomLogger constructor(val ctx: Context, embEnabled: Boolean = false) : Logger {
private val loggers = arrayListOf<Logger>()
fun get() = loggers
init {
if (embEnabled)
loggers.add(Emb(ctx))
if (BuildConfig.DEBUG)
loggers.add(DebugLogger(ctx))
}
override fun logError(t: Throwable, msg: String?) {
loggers.forEach { logger ->
logger.logError(t, msg)
}
}
}
enter code here
Here I am trying to mock get() or init{}
that was on dam question but i got you
note this can only be used in unittest as mockito static mock is not support on Android JVM
testImplementation "org.mockito:mockito-inline:4.8.1" you gonna need
this so added
Update you need to call this i forgot to add it sorry in your test case before call the method
Mockito.mockStatic(Class.forName("com.udacity.project4.locationreminders.RemindersActivityKt"))
fun getMockForMethod(clazz: Class<*>, methodName: String, methodResponse: Any) {
val method: Method = clazz.getMethod(methodName)
Mockito.`when`(method.invoke(null)).thenReturn(methodResponse)
}
now i created the method to handle no argument methods you can modifiy it as you see fit just pass the class using it name
getMockForMethod(Class.forName("com.udacity.project4.locationreminders.RemindersActivityKt"),
"doSomething","New Response")
Assert.assertEquals("New Response", doSomething())
works like a charm Enjoy 😁
i have updated the above code for anyone to use with static members in kotlin
your updates makes this easy to do now it is a class that you can mock entirly and easliy mock any methods
val loggerMock= Mockito.mock(Logger::class.java)
Mockito.`when`(loggerMock.loggers).thenReturn(new array of loggers)
I can't change project code, I can only write tests.
I have a function for testing:
fun funToBeTested() {
ClasWithWorkManager().veryVeryBadFunc(TAG)
// a lot of code to be tested below
...
}
and class with WorkManager:
class ClasWithWorkManager() {
companion object {
#JvmStatic
fun veryVeryBadFunc(tag: String) {
WorkManager.getInstance().cancelAllWorkByTag(tag)
WorkManager.getInstance().pruneWork()
}
}
}
There are no Android class references in the rest of this func.
Can I somehow cover with Unit test all funToBeTested() except of its first line?
Right now I have obvious WorkManager is not initialized properly. problem
I have seen other questions here related to this error but still not able to fix. Plus I'm asking this question because unlike other questions here, I'm getting this error only when I convert the code to Kotlin from Java.
I'm using this same RxJava code in Java, it works fine.
I converted to Kotlin and it gives this error -
Kotlin Compilation Error : None of the following functions can be
called with the arguments supplied
var observable : Observable<Bitmap> = Observable.just(bitmap)
var observer:Observer<Bitmap> = Observer<Bitmap>() {
fun onSubscribe(d: Disposable) {
disposable = d
}
fun onNext(orientedBitmap:Bitmap) {
// do something
}
fun onError(e:Throwable) {
}
fun onComplete() {
}
}
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// It shows the error here
.subscribe(observer)
Can anyone explain what's wrong here?
It was a conflict in importing package. I'm also using Observer from architecture components. Import Observer of both the packages, it will be solved.
So, for this -
var observer:Observer<Bitmap> = Observer<Bitmap>()
It was taking the Observer from the architecture components.
I solved it by using it like this -
var observer = object : io.reactivex.Observer<Bitmap>