mockito, in koltin how to verify a static method - kotlin

Having a kotlin singleton static method
internal object TestSingleton {
#JvmStatic
fun staticMethod1 (str: String) {
println("+++ === +++ TestSingleton.staticMethod(), $str")
staticMethod2 (str)
}
#JvmStatic
fun staticMethod2 (str: String) {
println("+++ === +++ TestSingleton.staticMethod2(), $str")
}
}
In java test code:
#Test
public void test_staticMethod() {
try (MockedStatic<TestSingleton> theMock = Mockito.mockStatic(TestSingleton.class, CALLS_REAL_METHODS)) {
TestSingleton.staticMethod1("test");
theMock.verify(() -> TestSingleton.staticMethod2(eq("test")), times(1));
}
}
it runs fine
but convert to kotlin it does not compile:
#Test
open fun test_staticMethod() {
Mockito.mockStatic(TestSingleton::class.java, Mockito.CALLS_REAL_METHODS).use { theMock ->
staticMethod1("test")
theMock.verify(() -> TestSingleton.staticMethod(any(Context.class), "test"), times(1))
// or
theMock.verify(times(1), () -> TestSingleton.staticMethod(any(Context.class)) )
}
}
having mockito version testImplementation "org.mockito:mockito-inline:3.12.4".
How to test static method using mockito in kotlin?
Not tried mockk yet since having a lot tests have been working with mockito. Not sure how simple with mockk in this case.

Here's how to do it in mockk (I highly recommend switching away from Mockito, mockk is just so much easier):
import TestSingleton.staticMethod1
import io.mockk.every
import io.mockk.just
import io.mockk.mockkStatic
import io.mockk.runs
import io.mockk.verify
import org.junit.jupiter.api.Test
internal object TestSingleton {
#JvmStatic
fun staticMethod1(str: String) {
println("+++ === +++ TestSingleton.staticMethod(), $str")
staticMethod2(str)
}
#JvmStatic
fun staticMethod2(str: String) {
println("+++ === +++ TestSingleton.staticMethod2(), $str")
}
}
class StackSign {
#Test
fun test_staticMethod() {
mockkStatic(TestSingleton::class)
every { TestSingleton.staticMethod2("test") } just runs
staticMethod1("test")
verify(exactly = 1) { TestSingleton.staticMethod2("test") }
}
}
BTW, add this to your build.gradle.kts
testImplementation("io.mockk:mockk:1.12.3")

Related

Testing custom exceptions in Kotlin with JUnit

I have the following class
data class CarDefects(
private val _carModel: CarModel,
private val _affectedYearsOfIssue: List<Year>,
private val _defectCode: String
) {
init {
validateDefectCode(_defectCode)
}
}
Validating function
fun validateDefectCode(defectCode: String) {
val pattern = Pattern.compile("^[a-zA-Z0-9-]*\$")
val m = pattern.matcher(defectCode)
if (defectCode.length !in 4..4) {
throw InvalidDefectCodeException(defectCode, "Defect code must be 4 characters long")
}
if (!m.matches()) {
throw InvalidDefectCodeException(defectCode, "Defect code can only contain alphanumeric characters")
}
}
And the exception class:
class InvalidDefectCodeException(_defectCode:String, message:String):
IllegalArgumentException("Invalid defect code $_defectCode. $message") {
}
I'm trying to test the validating function with JUnit
import car.exceptions.InvalidDefectCodeException
import car.validators.carDefectsValidators.validateDefectCode
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import java.time.Year
import kotlin.test.assertFailsWith
internal class CarDefectsTest {
val carModel = CarModel(Brand.BMW, "X5", 199219)
val carModel2 = CarModel(Brand.AUDI, "X434", 199219)
val defect = CarDefects(carModel, listOf(Year.of(2020), Year.of(2021)), "SE2#")
val defect2 = CarDefects(carModel2, listOf(Year.of(2020), Year.of(2021)), "122F4")
#Test
fun testDefectCodeExceptions() {
val exception = Assertions.assertThrows(InvalidDefectCodeException::class.java) {
validateDefectCode(defect.getDefectCode())
}
}
#Test
fun testDefectCodeExceptions2() {
assertFailsWith<InvalidDefectCodeException> {
validateDefectCode(defect2.getDefectCode())
}
}
}
Both tests fail, however expected exceptions are still thrown, from what i understand shouldn't both tests pass?
I've already seen the following post: Test expected exceptions in Kotlin
Inside class CarDefects, you're having this init block:
init {
validateDefectCode(_defectCode)
}
Hence, the exception will be thrown during construction.
Let's test the constructor instead with a stripped down CarDefects class. The following tests are passing on my computer.
import car.exceptions.InvalidDefectCodeException
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import kotlin.test.assertFailsWith
data class CarDefects(
private val defectCode: String
) {
init {
validateDefectCode(defectCode)
}
}
internal class CarDefectsTest {
#Test
fun testDefectCodeExceptions() {
Assertions.assertThrows(InvalidDefectCodeException::class.java) {
CarDefects(defectCode = "SE2#")
}
}
#Test
fun testDefectCodeExceptions2() {
assertFailsWith<InvalidDefectCodeException> {
CarDefects(defectCode = "122F4")
}
}
}

How to mock extensions function in kotlin

I am using Mockk in my project. I am trying to mock my extension function but it cannot find my function. I tried some piece of code but it cannot find the extension function inside my test. Can someone guide me, How can I solve this issue. Thanks
ExploreConsultationsActivity.kt
class ExploreConsultationsActivity : AppCompactActvity () {
... // more function
internal fun setupExploreConsultationVisibility(hasFocus: Boolean) {
if (hasFocus) {
.....
} else if (viewModel.queryText.isEmpty()) {
binding.consultationViewSwitcher.displayConsultationViewSwitcherChild(0)
}
}
internal fun ViewSwitcher.displayConsultationViewSwitcherChild(childNumber: Int) {
visible()
displayedChild = childNumber
}
}
ExploreConsultationsActivityTest.kt
class ExploreConsultationsActivityTest {
#get:Rule
val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
private val subject by lazy { spyk(ExploreConsultationsActivity()) }
#MockK private lateinit var mockConsultationViewSwitcher: ViewSwitcher
#Before
fun setUp() {
MockKAnnotations.init(this, relaxed = true)
setupMockView()
}
private fun setupMockView() {
mockkStatic(ExploreConsultationsLayoutBinding::class)
every { mockRootView.findViewById<ChipGroup>(R.id.exploreConsultationChips) } returns mockChipGroup
}
#Test
fun `setupExploreConsultationVisibility - `() {
// STUBBING
mockViewModel.queryText = ""
every { mockViewModel.topicSelected } returns ConsultationTopicsArea.ALL
with(mockConsultationViewSwitcher){
any<ViewSwitcher>().displayConsultationViewSwitcherChild(0)
}
// EXECUTION
subject.setupExploreConsultationVisibility(false)
// VERIFICATION
verify {
mockViewModel.filterBy(ConsultationTopicsArea.ALL)
}
}
I am getting this error

InjectMocks doesn't work with Kotlin constructor arguments with default values

EDIT: I've created a ticket with mockito-kotlin here
I have a class defined like so:
package me.jpalacios.poc
class MyClass(
private val myDependency: MyDependency = MyDependency()
) {
fun run() {
myDependency.doSomething()
}
}
package me.jpalacios.poc
class MyDependency {
fun doSomething() {
println("I did something")
}
}
package me.jpalacios.poc
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.verify
#ExtendWith(MockitoExtension::class)
class MyClassTest {
#Mock
private lateinit var myDependency: MyDependency
#InjectMocks
private lateinit var myClass: MyClass
#Test
fun `Test InjectMocks`() {
myClass.run()
verify(myDependency).doSomething()
}
}
Looks like a test defined like so does not work because the mocks are not injected:
#ExtendWith(MockitoExtension::class)
class MyClassTest {
#Mock
private lateinit var dependency: MyDependency
#InjectMocks
private lateinit var underTest: MyClass
}
plugins {
kotlin("jvm") version "1.5.20"
}
group = "me.jpalacios"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib"))
testImplementation("org.junit.jupiter:junit-jupiter:5.7.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.2")
testImplementation("org.assertj:assertj-core:3.20.2")
testImplementation("org.mockito.kotlin:mockito-kotlin:3.2.0")
testImplementation("org.mockito:mockito-junit-jupiter:3.11.2")
}
tasks{
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
configurations["compileClasspath"].forEach { file: File ->
from(zipTree(file.absoluteFile))
}
}
compileKotlin {
kotlinOptions {
jvmTarget = "${JavaVersion.VERSION_11}"
}
}
test {
useJUnitPlatform()
}
}
Any thoughts as to why?
The output is:
I did something
Wanted but not invoked: myDependency.doSomething();
-> at me.jpalacios.poc.MyDependency.doSomething(MyDependency.kt:6) Actually, there were zero interactions with this mock.
Wanted but not invoked: myDependency.doSomething();
-> at me.jpalacios.poc.MyDependency.doSomething(MyDependency.kt:6) Actually, there were zero interactions with this mock.
at me.jpalacios.poc.MyDependency.doSomething(MyDependency.kt:6) at
me.jpalacios.poc.MyClassTest.Test InjectMocks(MyClassTest.kt:22) at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
Method) at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
...
I really am no expert in Mockito, but adding default values for constructor arguments literally says that you don't need to be passed a dependency, so I wouldn't be surprised if Mockito doesn't try to inject mocks in this case. It really sees a no-arg constructor available, so wouldn't you want it to use it?
In any case, I'm not even sure you need this at all. Why not just instantiate the class under test yourself, and pass it the mock where you need it?
#ExtendWith(MockitoExtension::class)
class MyClassTest {
#Mock
private lateinit var myDependency: MyDependency
#Test
fun `Test InjectMocks`() {
MyClass(myDependency).run()
verify(myDependency).doSomething()
}
}
If you need to share such initialization among multiple tests, you can use a lazy property, or a #BeforeTest method to create it.

startKoin in KoinTest-class throws "A KoinContext is already started"

I'm using "withTestAppliction" in one of my tests to test if the route works. Before all Tests the DB-Table "cats" should have no entries. To get the DAO I need Koin in this Test but if conflicts with "withTestAppliction" where Koin will also be startet and throws A KoinContext is already started
[Update]
I know I could use something like handleRequest(HttpMethod.Delete, "/cats") but I don't want to expose this Rest-Interface. Not even for testing.
#ExperimentalCoroutinesApi
class CatsTest: KoinTest {
companion object {
#BeforeClass
#JvmStatic fun setup() {
// once per run
startKoin {
modules(appModule)
}
}
#AfterClass
#JvmStatic fun teardown() {
// clean up after this class, leave nothing dirty behind
stopKoin()
}
}
#Before
fun setupTest() = runBlockingTest {
val dao = inject<CatDAO>()
dao.value.deleteAll()
}
#After
fun cleanUp() {
}
#Test
fun testCreateCat() {
withTestApplication({ module(testing = true) }) {
val call = createCat(predictName("Pepples"), 22)
call.response.status().`should be`(HttpStatusCode.Created)
}
}
}
fun TestApplicationEngine.createCat(name: String, age: Int): TestApplicationCall {
return handleRequest(HttpMethod.Post, "/cats") {
addHeader(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
setBody(listOf(
"name" to name,
"age" to age.toString()
).formUrlEncode())
}
}
After test (after withTestApplication()) call KoinContextHandler.get().stopKoin().
Example: https://github.com/comm1x/ktor-boot/blob/master/test/common/common.kt
It looks similar to the issue I faced. The problem was that the module() passed under the withTestApplication() was trying to create the Koin object again. I replaced the module() with specific modules that I had to load for the tests except for the Koin.
Refer - test sample and
application sample
Had the same problem executing multiple tests in a class. After removing the init/startKoin (since it's initialized in the Application when you test with the emulator).
I am not really sure if this is the correct approach, but it kind of works for me and my build server.
#ExperimentalCoroutinesApi
#RunWith(AndroidJUnit4ClassRunner::class) // or JUnit4..
class MyTest : KoinTest {
private val mockedAppModule: Module = module(override = true)
factory { myRepo }
}
#Before
fun setup() {
loadKoinModules(mockedAppModule)
}
#After
fun tearDown() {
unloadKoinModules(mockedAppModule)
}
#Test
fun testSubscriberRegistration() = runBlockingTest { // only needed if you are using supend functions
// test impl...
}
}

Mocking ViewModel in Espresso

I'm writing Espresso UI test which mocks viewModel, referring GithubBrowserSample
what is the use of "TaskExecutorWithIdlingResourceRule", declaring Junit Rule will take care of IdlingResource?
Even after referring this "TaskExecutorWithIdlingResourceRule" class in my project whenever I build, compiler doesn't throw any error but when I run the test case it shows the Unresolved Error(s)
TaskExecutorWithIdlingResourceRule.kt
import androidx.arch.core.executor.testing.CountingTaskExecutorRule
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.IdlingResource
import org.junit.runner.Description
import java.util.UUID
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.TimeUnit
class TaskExecutorWithIdlingResourceRule : CountingTaskExecutorRule() {
// give it a unique id to workaround an espresso bug where you cannot register/unregister
// an idling resource w/ the same name.
private val id = UUID.randomUUID().toString()
private val idlingResource: IdlingResource = object : IdlingResource {
override fun getName(): String {
return "architecture components idling resource $id"
}
override fun isIdleNow(): Boolean {
return this#TaskExecutorWithIdlingResourceRule.isIdle
}
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
callbacks.add(callback)
}
}
private val callbacks = CopyOnWriteArrayList<IdlingResource.ResourceCallback>()
override fun starting(description: Description?) {
IdlingRegistry.getInstance().register(idlingResource)
super.starting(description)
}
override fun finished(description: Description?) {
drainTasks(10, TimeUnit.SECONDS)
callbacks.clear()
IdlingRegistry.getInstance().unregister(idlingResource)
super.finished(description)
}
override fun onIdle() {
super.onIdle()
for (callback in callbacks) {
callback.onTransitionToIdle()
}
}
}
Mocktest
#RunWith(AndroidJUnit4::class)
class MockTest {
#Rule
#JvmField
var activityRule = IntentsTestRule(SingleFragmentActivity::class.java, true, true)
#Rule
#JvmField
val executorRule = TaskExecutorWithIdlingResourceRule()
private lateinit var viewModel: SeriesFragmentViewModel
private val uiModelList = mutableListOf<SeriesBaseUIModel>()
private val seriesMutableLiveData = MutableLiveData<List<SeriesBaseUIModel>>()
private val seriesFragment = SeriesFragment()
#Before
fun init(){
viewModel = mock(SeriesFragmentViewModel::class.java)
`when`(viewModel.seriesLiveData).thenReturn(seriesMutableLiveData)
ViewModelUtil.createFor(viewModel)
activityRule.activity.setFragment(seriesFragment)
EspressoTestUtil.disableProgressBarAnimations(activityRule)
}
#Test
fun testLoading()
{
//Thread.sleep(3000)
uiModelList.add(ProgressUIModel())
seriesMutableLiveData.postValue(uiModelList.toList())
onView(withId(R.id.pod_series_recycler_view))
.check(selectedDescendantsMatch(withId(R.id.pod_adapter_series_header_title), isDisplayed()))
onView(withId(R.id.pod_series_recycler_view))
.check(selectedDescendantsMatch(withId(R.id.pod_adapter_series_header_title), withText(R.string.pod_series_header_title_text)))
onView(withId(R.id.pod_series_recycler_view))
.check(selectedDescendantsMatch(withId(R.id.pod_adapter_series_header_description), isDisplayed()))
onView(withId(R.id.pod_series_recycler_view))
.check(selectedDescendantsMatch(withId(R.id.pod_adapter_series_header_title), withText("Hello")))
Thread.sleep(5000)
}
}