Cannot mock final Kotlin class using Mockito 2
Solution 1
You may use Powermock for this, for example:
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.when;
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
@PrepareForTest({FinalClass1.class, FinalClass2.class})
public class Test {
@Rule
public PowerMockRule rule = new PowerMockRule();
... // your code here
}
Solution 2
PowerMock implements its own MockMaker
which leads to incompatibility with Mockito mock-maker-inline, even if PowerMock is just added as a dependency and not used. If two org.mockito.plugins.MockMaker
exist in path then any only one can be used, which one is undetermined.
PowerMock can however delegate calls to another MockMaker, and for then tests are run without PowerMock. Since PowerMock 1.7.0 this can be configured with using the PowerMock Configuration.
The MockMaker can be configured by creating the file org/powermock/extensions/configuration.properties
and setting:
mockito.mock-maker-class=mock-maker-inline
Example of using Mockito mock-maker-inline with PowerMock: https://github.com/powermock/powermock-examples-maven/tree/master/mockito2
Solution 3
Since Mockito 2.1.0 there is a possibility to mock final types, enums, and final methods. It was already mentioned in the comments to the original question.
To do this, you’ll need to create a folder (if dont exist) test/resources/mockito-extensions
and add there file with the name org.mockito.plugins.MockMaker
and this line:
mock-maker-inline
Links to documentation and tutorial
Solution 4
mock-maker-inline
works as is pointed out in other answers. But It's really slow. You can use the all-open
plugin to avoid this problem.
To do so you need:
- Create an Annotation:
annotation class Mockable
- Activate all-open in your
build.gradle
file:
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
apply plugin: 'kotlin-allopen'
allOpen {
annotation('com.example.Mockable')
}
- Annotate the classes that you want to mock:
@Mockable
class Foo {
fun calculateTheFoo(): Int {
sleep(1_000) // Difficult things here
return 1
}
}
If you want more information you can read my blog post where I explain this with more details: Mocking Kotlin classes with Mockito — the fast way
Solution 5
Try adding this below dependency to your build.gradle.
testImplementation 'org.mockito:mockito-inline:2.8.47'
Replace with your mockito version instead of 2.8.47. This will help you to avoid using powermock for the issue.
please look into the below link to know how this thing works.
Related videos on Youtube
blackpanther
Skills: Java, Kotlin, Groovy Mobile: Android MVVM / MVP Android architectures Retrolambda, Java 8 RxJava 1 & 2 - love beautiful Presenters written in RxJava Unit Testing and TDD with JUnit, Robolectric, Mockito, and Instrumentation testing REST/JSON Room and Realm for Android persistence Databases: MySQL, PostgreSQL, Intersystems Cache OODB, Oracle, MongoDB Build tools: Gradle & Maven Git Linux/UNIX experience (minimal shell-scripting experience) Wrote an article on developing a Clean architecture using MVVM & RxJava 2 Education: BSc Computing and Information Systems (University of London - First-class Honours June 2013) Coursera course: Creative Programming for Digital Media and Mobile Apps Coursera course: Programming Cloud Services for Android Handheld Systems
Updated on September 14, 2022Comments
-
blackpanther over 1 year
I am unable to mock a Kotlin final class using Mockito 2. I am using Robolectric in addition.
This is my test code:
@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class Test { // more mocks @Mock MyKotlinLoader kotlinLoader; @Before public void setUp() { MockitoAnnotations.initMocks(this); } }
The test fails when we try to initialise the mocks in the
setUp()
method.In addition, I am using the following gradle dependencies in my code:
testCompile 'org.robolectric:robolectric:3.3.2' testCompile 'org.robolectric:shadows-multidex:3.3.2' testCompile 'org.robolectric:shadows-support-v4:3.3.2' testCompile("org.powermock:powermock-api-mockito2:1.7.0") { exclude module: 'hamcrest-core' exclude module: 'objenesis' } testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-inline:2.8.9'
All other unit tests pass using this configuration but as soon as I try to mock the Kotlin class it throws the following error:
Mockito cannot mock/spy because : - final class
Please note I am using Mockito version 2 and I am using the
inline
dependency which automatically enables the ability to mock final classes.-
Seelenvirtuose almost 7 yearsDid you follow the documentation and create the file
/mockito-extensions/org.mockito.plugins.MockMaker
containing the valuemock-maker-inline
? -
blackpanther almost 7 yearsYou don't need to do that with this dependancy:
testCompile 'org.mockito:mockito-inline:2.8.9'
-
Eugene Brusov over 6 yearsWhere it's stated that we don't have to use
/mockito-extensions/org.mockito.plugins.MockMaker
with mockito 2.8.9? Once I removed that file I started got the same error, so looks like it's still necessary to use that file even with mockito 2.8.9. -
yasd about 6 yearsFor me it works as @blackpanther suggests (using
2.+
instead of2.8.9
). @EugeneBrusov: It's written: here (in the meantime) ... As a convenience, the Mockito team provides an artifact where this mock maker is preconfigured. Instead of using the mockito-core artifact, include the mockito-inline artifact in your project. Note that this artifact is likely to be discontinued once mocking of final classes and methods gets integrated into the default mock maker. -
Brais Gabin about 6 yearsThe
mock-maker-inline
works but it's really slow I recommend you to use the all-open Compiler Plugin from Kotlin -
ThomasW over 4 years@BraisGabin you should write up your comment as an answer.
-
blackpanther over 4 yearsIf anyone wants a better way - just use Mockk. It's made for Kotlin.
-
-
blackpanther almost 7 yearsYeah that works - it seems as if in some circumstances where the Mockito v2 final class mocks don't work, this is the only possible solution.
-
blackpanther over 5 yearsIt's a valid option - however, Kotlin keeps these classes closed by default and it is nice to keep them closed as it defines our intent better.
-
Willi Mentzel over 3 yearsadding the mockito-inline dependency as shown here: stackoverflow.com/a/56320512/1788806 seems cleaner.