Android Annotation Processor: Invoke annotated method with a condition - android

I'm quite new to building a custom annotation processor,
I've followed some tutorials & guides over here on SO too but been stuck on adding a condition for the annotated method.
The problem:
I have a custom annotation directed only for methods,
say, #RUN(isDebug: Boolean) & the method would be:
#RUN(isDebug = true)
private fun runInDebugOnly() {
....
}
#RUN(isDebug = false)
private fun runInReleaseOnly() {
....
}
So in my Annotation Processor,
is it possible to execute these functions with a condition?
I know the concept of generating a custom class & methods inside it,
But how to exactly intercept the method & use the generated method instead.
Any guidance would be appreciated.

An annotation processor only runs at compile time, and is usually used to generate new classes or code.
Sounds to me like you want to generate a new method at compile time which will call the correct annotated method depending on the build type.
e.g. when you run a debug build you want to have
fun myNewMethod() {
runInDebugOnly()
}
but when you run a release build you want to have
fun myNewMethod() {
runInReleaseOnly()
}
The rest of your app will just call myNewMethod() and it won't care about the implementation.
You could achieve this another way without using an annotation processor
fun myNewMethod() {
if (Build.DEBUG) {
runInDebugOnly()
} else if (Build.RELEASE) {
runInReleaseOnly
}
}
Is this the kind of thing you're after?

Related

KDoc / Dokka: Ignore inherited methods in subclass

I am generating documentation using KDoc/Dokka for an android library.
I have a custom view, which extends LinearLayout.
The problem is that LinearLayout contains hundreds of public methods. Dokka generates empty documentation for all of these methods, even though I did not use or override them in my own code.
This completely buries any of my own methods and makes the documentation near useless.
How can I prevent dokka from generating documentation for inherited methods?
Currently this is not supported, probably we will add some flag to turn it on/off.
You can follow this issue: https://github.com/Kotlin/dokka/issues/1501
From the answer by #andrzej-ratajczak the following can be used
pluginsMapConfiguration.set(
["org.jetbrains.dokka.base.DokkaBase": """{ "separateInheritedMembers": true}"""]
)
here an example of my own module
dokkaHtml {
moduleName = "${project.name}"
dokkaSourceSets {
configureEach {
// Suppress a package
perPackageOption {
// will match all packages and sub-packages
matchingRegex.set(".*\\.internal.*")
suppress.set(true)
}
// separate inherited members to avoid polluting our public API
// https://github.com/Kotlin/dokka/issues/1501
pluginsMapConfiguration.set(
["org.jetbrains.dokka.base.DokkaBase": """{ "separateInheritedMembers": true}"""]
)
}
}
}

Kotlin - Mockito verify method calls

I'm trying my hand with Mockito for writing unit test's. I have a class that needs to be tested like below-
open class Employee {
fun setDetails(name: String, age: Int) {
setName(name)
setAge(age)
}
fun setName(name: String) { }
fun setAge(age: Int) { }
}
Below is my test class
class EmployeeTest {
#Mock
lateinit var emp: Employee
#Before
fun setup() {
MockitoAnnotations.initMocks(this)
}
#Test
fun testDetail() {
emp.setDetails("Henry", 23)
verify(emp, times(1)).setAge(23)
}
}
Here is my problem
When I do -
verify(emp, times(1)).setAge(23)
This give's me a success, because setAge is called once in setDetails() of Employee.kt. So that works fine for me
But, when I do-
verify(emp, never()).setAge(23)
This still gives me a success, even though the method is called in setDetails(). Shouldn't this test case fail?
Please help me understand this. I haven't been able to figure out why this happens.
EDIT
Here's what worked for me
I used a spy instead of a mock. However, I had to also declare the methods as open in Kotlin.
As mentioned by #kcoppock, your question includes an improper use of a mock. You should be using mocks to stub out dependencies in order to control their behavior.
In your case, the unit under test is the Employee class and its associated methods. In general, you do not want to mock out the unit under test because you want to know (from your unit test) if your class is behaving the way it should. To accomplish that, you'll want to use a real instance of an Employee, and not a mock.
If you are insistent on using verify on the Employee instance, you can create a spy.
#Test
fun setDetails_adjustsAge() {
val employee = spy(Employee())
employee.setDetails("Henry", 23)
assertEquals(23, employee.age)
verify(emp, times(1)).setAge(23)
}
Here are some references for further reading:
Mockito official documentation on spies:
http://static.javadoc.io/org.mockito/mockito-core/2.24.0/org/mockito/Mockito.html#13
Tutorial on how to use Mockito.spy
https://www.baeldung.com/mockito-spy
Differences between mock and spy: https://www.toptal.com/java/a-guide-to-everyday-mockito
So your issue here is that you don't actually want to use a mock. When you use a mock, you're required to define the behavior for any method that you call on that instance. So when you call emp.setDetails("Henry", 23), there is no implementation for that method so nothing happens. The behavior defined in the Employee class will not be used, as emp is just a fake instance of Employee that has not defined any behavior.
For your test scenario, you should prefer to use a real instance, and validate the end result rather than the internal behavior. For instance:
#Test
fun setDetails_adjustsAge() {
val employee = Employee()
employee.setDetails("Henry", 23)
assertEquals(23, employee.age)
}

Mockito Stubbed Spy sometimes calls and sometimes does not call the spied object methods

QUESTION
In a #Test, how can I achieve both;
Call a real method from a Kotlin class under test and
stub the inner calls it does to other methods within such class under test.
SCENARIO
I am using the following libraries;
testCompile "com.nhaarman:mockito-kotlin:1.5.0"
testCompile "org.mockito:mockito-inline:2.12.0"
I also have a simple kotlin class in the form of
class MyClass() {
fun parentFunc() {
funA()
}
fun funA() {
//DOES SOMETHING WHICH I ASSUME IS IRRELEVANT FOR ANSWERING THE QUESTION
}
}
TESTING WITH A SPY
#Test
fun myTest() {
val myClassSpy = spy(MyClass())
Mockito.doNothing().`when`(myClassSpy.funA())
//Mockito.doNothing().whenever(myClassSpy.funA()) also throws the same error
myClassSpy.parentFunc()
verify(myClassSpy, times(1)).funA()
}
Which throws the error,
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.nhaarman.mockito_kotlin.MockitoKt.doNothing(Mockito.kt:108)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
Another Test case;
#Test
fun myTest() {
val myClassSpy = Mockito.spy(MyClass())
myClassSpy.parentFunc()
verify(myClassSpy, times(1)).funA()
}
gives the following error:
Wanted but not invoked:
myClass.funA();
However, there was exactly 1 interaction with this mock:
myClass.parentFunc();
Also, anytime I attempt to use the debugger to call myClassSpy methods or something related to it, it throws the following error:
com.sun.jdi.InternalException : Unexpected JDWP Error: 41
I have attempted to use
Mockito.`when`(myClassSpy.funA()).then { }
Mockito.`when`(myClassSpy.funA()).thenAnswer { }
Mockito.`when`(myClassSpy.funA()).thenReturn(Unit)
TESTING WITH A MOCK
Mocking the whole class does not work in this case because it is a mock and does not call the real method under test:
#Test
fun myTest() {
val myMock: MyClass = mock()
myMock.parentFunc()
verify(myMock, times(1)).funA()
}
Same error:
Wanted but not invoked:
myClass.funA();
However, there was exactly 1 interaction with this mock:
myClass.parentFunc();
If I further call the real method it also shows the same wanted but not invoked myClass.funA(); error:
#Test
fun myTest() {
val myMock: MyClass = mock()
Mockito.`when`(myMock.parentFunc()).thenCallRealMethod()
myMock.parentFunc()
verify(myMock, times(1)).funA()
}
I also, tried opening MyClassbut threw the same errors.
Thus, how can I stub methods from a spy so that when I test methods from such spied object it does not propagate the call to other methods which I do not want to further mock.
Any help, suggestion, idea... in order to test these type of methods is highly appreciated.
for me mocking kotlin classes with mockito sometimes works and sometimes it does not, so what i usually do is extract an for that class, and that always works.
also mocks are for testing interactions so you should not mock a method on the same class that you are testing but a different class that you pass to the constructor (dependency injection) then you can inject a mock at test time and the correct dependency at production time.
class MyTestedClass(val funManager:MyUsedClass) {
fun parentFunc() {
funManager.funA()
}
}
Solution:
When the code base is all in Kotlin the solution was:
Use MockK which is a library for testing and mocking Kotlin Code.
When the code base is written in both Java and Kotlin the solution was:
Use MockK for testing & mocking Kotlin and Mockito for testing & mocking Java code.

Return internal class calling object function

I'm testing with Kotlin and I'm writing a small library to be imported and used by a test App project.
In the library project I marked my classes as internal because I don't want them to be visible for the App project, but I would like to have a single entry point for the library, and for that I am using a Kotlin object like shown below
LIBRARY
object Library {
fun getComponent() = AwesomeComponent()
}
internal class AwesomeComponent() {
// some implementation
}
TEST APP
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = Library.getComponent()
}
}
The problem is that this doesn't compile because the function in the Library object returns an internal type and therefore need to be marked as internal as well, but doing so would hide the function from the TestApp.
Another option would be to not have the internal modifier at all so the TestApp can see the Library method, but then it can also see the classes inside the Library project
Is there an easy solution that I am overlooking here or does it need to go through re-planning of packages and structure of the Library project? (not sure how to do it in that case)
You have to publish some sort of public API for the app module to be able to use the component that the getComponent() method returns. If you want to publish minimal information about your library, you can have it return an interface that contains only the publicly available method calls to the library, and make your class implement that interface:
object Library {
fun getComponent(): IAwesomeComponent = AwesomeComponent()
}
interface IAwesomeComponent {
// methods you want to call on the component in the app module
}
internal class AwesomeComponent(): IAwesomeComponent {
// implementations of the interface methods
}

Annotate interface function that must call super

I'm creating interface and some function in it has a body.
It's required, that class that implements this interface must call super in overriding function before executing other code.
How can I do this?
interface Watcher {
fun funWithoutBody()
fun startWatching() {
//do some important stuff which must be called
}
}
I've accidentally found, what I was looking for. It's a #CallSuper annotation available in androidx.annotation package. Docs
Use the #CallSuper annotation to validate that an overriding method
calls the super implementation of the method. The following example
annotates the onCreate() method to ensure that any overriding method
implementations call super.onCreate():
#CallSuper
protected fun onCreate(savedInstanceState: Bundle?) {
}
Android Studio / IntelliJ IDEA does this sort of thing in some cases but it isn't done through annotations but through code inspection.
e.g. MissingSuperCall is an Android Lint Check for which IntelliJ IDEA supports (Integration with Android Lint tool in IntelliJ IDEA 11.1 | IntelliJ IDEA Blog).
You can create your own custom inspection if you are using Android Studio or IntelliJ IDEA: IntelliJ IDEA 2016.2 Help :: Creating Custom Inspections.
You could just make startWatching abstract and call it in another function. E.g.:
interface Watcher {
fun funWithoutBody()
fun userDefinedStartWatching()
fun startWatching() {
//insert code you'd normally want to be called when using super()
userDefinedStartWatching()
}
}

Categories

Resources