toothpick
toothpick copied to clipboard
Mockk/TP issue
After a bunch of analysis, we identify what's wrong between TP and Mockk.
Here is the app code:
class Tooth @Inject constructor() {
@Inject
lateinit var pick: Pick
fun foo() = pick.bar()
}
class Pick @Inject constructor() {
@Inject
lateinit var smoothie: ISmoothie
fun bar() = smoothie.olive()
}
interface ISmoothie {
fun olive(): Int
}
class Smoothie @Inject constructor() : ISmoothie {
override fun olive() = 42
}
Here the full TP case, no mock : OK
class FullTP {
val toothPickRule = ToothPickRule(this, "Foo")
@Rule get
lateinit var tooth: Tooth
@Before
fun setUp() {
toothPickRule.scope.installModules(module {
bind(ISmoothie::class.java).to(Smoothie::class.java)
})
toothPickRule.inject(this)
tooth = toothPickRule.getInstance(Tooth::class.java)
}
@Test
fun calculateAddsValues1() {
Assert.assertEquals(42, tooth.foo())
}
}
Now, if I want to mock Smoothie:
//Should work
class SmoothieMockFailed {
val toothPickRule = ToothPickRule(this, "Foo")
@Rule get
lateinit var tooth: Tooth
@MockK
lateinit var smoothie: ISmoothie
@Before
fun setUp() {
MockKAnnotations.init(this)
toothPickRule.inject(this)
tooth = toothPickRule.getInstance(Tooth::class.java)
}
@Test
fun calculateAddsValues1() {
every { smoothie.olive() } returns 41
Assert.assertEquals(41, tooth.foo())
}
}
The error is the following :
toothpick.locators.NoFactoryFoundException: No factory could be found for class fr.afaucogney.mobile.android.app.proapp.ISmoothie. Check that the class has either a @Inject annotated constructor or contains @Inject annotated members.
This testcase should work.
To hack it and make it work, we have to use this testcase:
class HackOne {
val toothPickRule = ToothPickRule(this, "Foo")
@Rule get
// @Inject
lateinit var tooth: Tooth
@MockK
@field:MockK
lateinit var smoothie: ISmoothie
@Before
fun setUp() {
MockKAnnotations.init(this) // turn relaxUnitFun on for all mocks
toothPickRule.inject(this)
tooth = toothPickRule.getInstance(Tooth::class.java)
}
@Test
fun calculateAddsValues1() {
every { smoothie.olive() } returns 41
Assert.assertEquals(41, tooth.foo())
}
}
This one work because of dual annotation. @field:Mockk to let TP bind the ISmoothieMock and @Mockk to let Mock running the stub.
In fact the @Mockk annotation target property (or target nothing, and the property is by default) The @Mock target field, so TP succeed to do its binding
I do not know if this is the job of TP or Mockk to fix this. I have been in contact with the guy from mockk that fairly said "not very experienced with this annotations"
I will ask him to join.
Here are the decompiled koltin to java code
When only @field:MockK
@MockK
@NotNull
public ISmoothie smoothie;
When only @MockK
@MockK
public static void smoothie$annotations() {
}
When both:
@MockK
@NotNull
public ISmoothie smoothie;
...
@MockK
public static void smoothie$annotations() {
}
Hey @afaucogney thx for the detailed example!
The Mock
annotation for easymock and mockito has Target = FIELD
. That means that on the bytecode the annotation goes to the field. For MockK, the annotation MockK
does not have a target, so it goes to the method.
Because of the issue https://github.com/stephanenicolas/toothpick/issues/334, we thought that the MockK
annotation already worked. We should have added a unit test...
I see 2 options:
- Mockk adds the target to the annotation to make it equivalent to Easymock and Mockito
- We modify the rule to read methods of the type
XYZ$annotations
andMockK
annotation. Then look for the field with nameXYZ
.
Taking into account that you are already in contact with the MockK author. Would you be able to ask him why he does not have a target and if he would be willing to do so? Otherwise, we can change the rule.
What happens when an annotation has multiple targets ? I couldn't find any doc about that..
On Sun, Sep 22, 2019, 10:58 Daniel Molinero, [email protected] wrote:
Hey @afaucogney https://github.com/afaucogney thx for the detailed example!
The Mock annotation for easymock and mockito has Target = FIELD. That means that on the bytecode the annotation goes to the field. For MockK, the annotation MockK does not have a target, so it goes to the method.
Because of the issue #334 https://github.com/stephanenicolas/toothpick/issues/334, we thought that the MockK annotation already worked. We should have added a unit test...
I see 2 options:
- Mockk adds the target to the annotation to make it equivalent to Easymock and Mockito
- We modify the rule to read methods of the type XYZ$annotations and MockK annotation. Then look for the field with name XYZ.
Taking into account that you are already in contact with the MockK author. Would you be able to ask him why he does not have a target and if he would be willing to do so? Otherwise, we can change the rule.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/stephanenicolas/toothpick/issues/373?email_source=notifications&email_token=AAN7PXLCE6FUWLW53MDBPODQK6W5ZA5CNFSM4IYAVIAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7JLPJI#issuecomment-533903269, or mute the thread https://github.com/notifications/unsubscribe-auth/AAN7PXN2GLW5WM2VFTAWTJ3QK6W5ZANCNFSM4IYAVIAA .
Yeah, i couldn't see any doc either. Just checking the generated bytecode 😕
@afaucogney were you able to sync with the MockK author?
Yes, but he is under project pressure, so he has only time to manage critical bug for now....
From a general matter, which should adapter to the other ! Di or Mock ? What is your opinion ?
Le ven. 18 oct. 2019 à 13:13, Daniel Molinero [email protected] a écrit :
@afaucogney https://github.com/afaucogney were you able to sync with the MockK author?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/stephanenicolas/toothpick/issues/373?email_source=notifications&email_token=ABSAEWPT7ID635INLCCWNELQPHVDXA5CNFSM4IYAVIAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBVF4VY#issuecomment-543841879, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSAEWMVWWH46YNE2OJ6LOTQPHVDXANCNFSM4IYAVIAA .
My question is why the annotation @MockK
does not have a target. If it is something that they are missing, then they could fix it. If it is intended not to have it, we can fix it on our side.