Mockk unmockk() method is not destroying mocks - kotlin

I am running the following test code, but in some weird way I am not understanding it is not clearing the mocks in between the two tests.
I am getting environmentAsRequired as a return of repo.getClusterEnvironment(environmentName) on the second test instead of it throwing NoResultException() which causes the second test to fail because the exception I am asserting does not get thrown.
I have already verified and the method annotated with #BeforeEach is being called in between tests.
Anyone has any idea?
class ConfigUtilsTest {
#BeforeEach
fun start() {
MockKAnnotations.init(this)
val testModule = module() {
single { mockk<Repository>() }
single { mockk<AdminClient>() }
}
startKoin { modules(testModule) }
}
#AfterEach
fun stop() {
stopKoin()
unmockkAll()
}
#Test
fun `fetchOrCreateCluster creates the cluster if it is not on db but all required attributes are`() {
val environmentName = "environmentForCluster"
val clusterName = "NameForCluster"
val clusterURL = "UrlForCluster"
val environmentAsRequired = ClusterEnvironment(0, environmentName)
val expectedResult = Cluster(
clusterURL,
clusterName,
environmentAsRequired,
)
val repo = get(Repository::class.java)
every { repo.getCluster(clusterURL) } throws NoResultException()
every { repo.getClusterEnvironment(environmentName) } returns environmentAsRequired
fetchOrCreateCluster(
clusterName,
environmentName,
clusterURL
) shouldBe expectedResult
}
#Test
fun `fetchOrCreateCluster throws InvalidConfigException creating cluster if required attribute (environment) is not on DB`() {
val clusterName = "NameForCluster"
val clusterURL = "UrlForCluster"
val repo = get(Repository::class.java)
every { repo.getCluster(clusterURL) } throws NoResultException()
every { repo.getClusterEnvironment(environmentName) } throws NoResultException()
shouldThrow<InvalidConfigException> {
fetchOrCreateCluster(
clusterName,
environmentName,
clusterURL
)
}
}
}

Related

Kafka listener not working for spring test

I have the following class to handle account update kafka events
#Component
class AccountUpdateQueueListener(
private val accountUpdateUseCase: AccountUpdateUseCase
) {
#KafkaListener(
topics = [Topics.ACCOUNT_UPDATES],
groupId = "api",
containerFactory = "accountUpdatesConsumeFactory",
)
fun onAccountUpdate(
#Payload request: AccountUpdateEventMessage,
#Header("_txId") txId: String,
acknowledgment: Acknowledgment
) {
MDC.put("account_id", request.account.id.toString())
MDC.put("_txId", txId)
try {
accountUpdateUseCase.update(AccountUpdateEventMessage.toCommand(request))
acknowledgment.acknowledge()
} catch (e: AccountNotFoundException) {
...
}
}
I am trying to test the accountUpdateUseCase.update function used in onAccountUpdate function is called with this test class
#SpringBootTest
#ActiveProfiles("testing")
#DirtiesContext
#EmbeddedKafka(partitions = 1, brokerProperties = ["listeners=PLAINTEXT://localhost:9092", "port=9092"])
class AccountUpdateQueueListenerTest {
#MockK
lateinit var accountUpdateUseCase: AccountUpdateUseCase
#InjectMockKs
lateinit var accountUpdateQueueListener: AccountUpdateQueueListener
#Autowired
lateinit var kafkaTemplate: KafkaTemplate<String, AccountUpdateEventMessage>
val accountUpdateEventMessage = AccountUpdateEventMessage(
...
)
#BeforeEach
fun init() {
MockKAnnotations.init(this)
}
#Test
fun `no errors and update is entered`() {
kafkaTemplate.send(Topics.ACCOUNT_UPDATES, "", accountUpdateEventMessage)
verify(exactly = 1) { accountUpdateUseCase.update(any()) }
}
}
The tests failed as accountUpdateUseCase.update. When running this with a debugger it is clear that onAccountUpdate is never entered. However, if the test case is rewritten as follows the debugger actually stopped in onAccountUpdate
#Test
fun `no errors and update is entered`() {
kafkaTemplate.send(Topics.ACCOUNT_UPDATES, "", accountUpdateEventMessage)
verify(exactly = 1) { accountUpdateQueueListener.onAccountUpdate(any(),any(),any()) }
}
My questions are why does the debugger stop in the second test case and how can I rewrite the first test to achieve my original goal?

Kotlin flows junit test freezes when to test collect method

I'm trying to write a component which uses different datasources of data.
Then data is combined and emitted in the different resulting flow.
class TaskControlComponent(
private val diskCacheDataSource: DiskCacheDataSource,
private val debugDataSource: DebugDataSource
) {
private val _localTasks = MutableStateFlow<Map<String, TaskItem>>(emptyMap())
val localTasks: StateFlow<Map<String, TaskItem>> = _localTasks
suspend fun loadLocal() {
flowOf(
diskCacheDataSource.defaultFeatures,
diskCacheDataSource.localFeatures,
debugDataSource.debugFeatures
).flattenMerge().collect {
computeLocalTasks()
}
}
private suspend fun computeLocalTasks() {
val resultTasks = HashMap<String, TaskItem>(64)
listOf(
diskCacheDataSource.defaultTasks,
diskCacheDataSource.localTasks,
debugDataSource.debugTasks
).forEach { tasksMap ->
tasksMap.value.forEach { entry ->
resultTasks[entry.key] = entry.value
}
}
_localTasks.emit(resultTasks)
}
}
DataSource
interface DiskCacheDataSource {
val defaultTasks: StateFlow<Map<String, TaskItem>>
val localTasks: StateFlow<Map<String, TaskItem>>
}
It works, but how to write junit test for that?
class TaskControlImplTest {
private lateinit var taskControl: TaskControlComponent
#Mock
lateinit var diskCacheDataSource: DiskCacheDataSource
#Mock
lateinit var debugDataSource: DebugDataSource
#Before
fun setup() {
MockitoAnnotations.initMocks(this)
taskControl = TaskControlComponent(diskCacheDataSource, debugDataSource)
}
#Test
fun testFeatureControl() {
whenever(diskCacheDataSource.defaultTasks).thenReturn(
MutableStateFlow(
mapOf(
"1" to TaskItem(
"1",
TaskStatus.On
)
)
)
)
whenever(diskCacheDataSource.localTasks).thenReturn(MutableStateFlow(emptyMap()))
whenever(debugDataSource.debugTasks).thenReturn(MutableStateFlow(emptyMap()))
runBlocking {
taskControl.loadLocal()
}
runBlocking {
taskControl.localTasks.collect {
Assert.assertEquals(it.size, 1)
}
}
}
}
In case of the following sequence of commands
runBlocking {
taskControl.loadLocal()
}
runBlocking {
taskControl.localTasks.collect {
Assert.assertEquals(it.size, 1)
}
}
test freezes, and runs forewer.
When I swap the pieces of code, first instead of second and the contrary
runBlocking {
featureControl.localFeatures.collect {
Assert.assertEquals(it.size, 1)
}
}
runBlocking {
featureControl.loadLocal()
}
Tests finishes with warning
expected:<0> but was:<1>
Expected :0
Actual :1
Is it possible to write test for such usecase? What should be investigated or done in order to make test workable?
The reason the order matters here is because StateFlow is hot, unlike normal Flow, meaning it starts running with data immediately not when it is collected
I test with the turbine library but you don't need it. I don't remember the exact setup of not using turbine but it was a bit more complicated so I chose to use turbine
https://github.com/cashapp/turbine

Kotlin - Don't run unit test until lateinit property is initialized

I have a case where the unit of code I'm testing is running on a different thread and so the test executes and fails before the unit has finished executing:
class Tests {
private lateinit var result: String
#BeforeAll
fun setup() {
DataService().subscribe {
result = it
}
}
#Test
fun `get result from data service`() {
assert(result.contains("Hello"))
}
}
When the test runs, I get the following exception:
kotlin.UninitializedPropertyAccessException: lateinit property result has not been initialized
How can I ensure that the tests don't run before result has been initialized?
The test in the provided form won't work: #BeforeAll is allowed only on static methods, but then it couldn't access the result field. It can be solved by using #BeforeEach and #TestInstance(TestInstance.Lifecycle.PER_METHOD) [but see the UPDATE at the end of the post]
Regarding the result initialization itself: you can check if it has been initialized using this::result.isInitialized, e.g.:
#TestInstance(TestInstance.Lifecycle.PER_METHOD)
class AwaitTest {
private lateinit var result: String
#BeforeEach
fun setup() {
DataService().subscribe {
result = it
}
}
#Test
fun `get result from data service`() = runBlocking {
awaitInitialization()
assert(result.contains("Hello"))
}
suspend fun awaitInitialization() {
while(!this::result.isInitialized) {
delay(100)
}
}
}
Alternatively you could use awaitility:
#TestInstance(TestInstance.Lifecycle.PER_METHOD)
class AwaitTest {
private lateinit var result: String
#BeforeEach
fun setup() {
DataService().subscribe {
result = it
}
}
#Test
fun `get result from data service`() {
await until { this#AwaitTest::result.isInitialized }
assert(result.contains("Hello"))
}
}
Or even better, move await to the setup() method:
#TestInstance(TestInstance.Lifecycle.PER_METHOD)
class AwaitTest {
private lateinit var result: String
#BeforeEach
fun setup() {
DataService().subscribe {
result = it
}
await until { this#AwaitTest::result.isInitialized }
}
#Test
fun `get result from data service`() {
assert(result.contains("Hello"))
}
}
UPDATE:
Actually a #BeforeAll method doesn't need to be static if the test class is annotated with the#TestInstance(TestInstance.Lifecycle.PER_CLASS):
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AwaitTest {
private lateinit var result: String
#BeforeAll
fun setup() {
DataService().subscribe {
result = it
}
await until { this#AwaitTest::result.isInitialized }
}
#Test
fun `get result from data service`() {
assert(result.contains("Hello"))
}
}

MockK - cannot mock same function twice

I am trying to test the getTopicNames function (below) in two scenarios: If it succeeds and if it does not succeed.
fun getTopicNames(): Either<Exception, Set<String>> =
try {
adminClient.listTopics()
.names()
.get()
.right()
} catch (exception: ExecutionException) {
exception.left()
}
This is the test class in which I am doing those two scenarios. If I run each test individually, they both suceed. If I run the entire class the second to execute fails because for some reason the previous mock on adminClient.listTopics() is being retained.
These are the versions for everything involved:
kotlin: 1.3.72
koin: 2.1.6
junit: 5.6.1
mockk: 1.10.0
class TopicOperationsTest {
#BeforeEach
fun start() {
val testModule = module(createdAtStart = true) {
single { mockk<AdminClient>() }
}
startKoin { modules(testModule) }
}
#AfterEach
fun stop() {
stopKoin()
}
#Test
fun `getTopicNames() returns a Right with the topics names`() {
val adminClient = get(AdminClient::class.java)
val listOfTopicsToReturn = mockk<ListTopicsResult>()
val expectedTopics = setOf("Topic1", "Topic2", "Topic3")
every { adminClient.listTopics() } returns listOfTopicsToReturn
every { listOfTopicsToReturn.names() } returns KafkaFuture.completedFuture(expectedTopics)
println("listOfTopicsToReturn.names(): " + listOfTopicsToReturn.names())
println("adminClient.listTopics(): " + adminClient.listTopics())
println("getTopicNames(): " + getTopicNames())
assertThat(getTopicNames().getOrElse { emptySet() }, `is`(expectedTopics))
}
#Test
fun `getTopicNames() returns a Left if failing to get topic names`() {
val adminClient = get(AdminClient::class.java)
every { adminClient.listTopics() } throws ExecutionException("Some Failure", Exception())
assertThat(getTopicNames(), IsInstanceOf(Either.Left::class.java))
}
}
This is the error I get, caused by the fact that the test that verifies the failure is the first to run:
java.lang.AssertionError:
Expected: is <[Topic1, Topic2, Topic3]>
but: was <[]>
Expected :is <[Topic1, Topic2, Topic3]>
Actual :<[]>
<Click to see difference>
Already tried clearAllMocks() on the BeforeEach method but it does not solve my problem as I just start getting:
io.mockk.MockKException: no answer found for: AdminClient(#1).listTopics()
I found a solution that makes everything work. It is a combination of:
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
Having the mock as a class object
MockKAnnotations.init(this) in the #BeforeEach method
clearMocks() specifying the actual mock to be cleared (should work for multiple mocks too, just separated by commas.
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TopicOperationsTest {
private var adminClientMock = mockk<AdminClient>()
#BeforeEach
fun start() {
MockKAnnotations.init(this)
val testModule = module(createdAtStart = true) {
single { adminClientMock }
}
startKoin { modules(testModule) }
}
#AfterEach
fun stop() {
clearMocks(adminClientMock)
stopKoin()
}
#Test
fun `getTopicNames() returns a Right with the topics names`() {
val adminClient = get(AdminClient::class.java)
val listOfTopicsToReturn = mockk<ListTopicsResult>()
val expectedTopics = setOf("Topic1", "Topic2", "Topic3")
every { adminClient.listTopics() } returns listOfTopicsToReturn
every { listOfTopicsToReturn.names() } returns KafkaFuture.completedFuture(expectedTopics)
assertThat(getTopicNames().getOrElse { emptySet() }, `is`(expectedTopics))
}
#Test
fun `getTopicNames() returns a Left if failing to get topic names`() {
val adminClient = get(AdminClient::class.java)
every { adminClient.listTopics() } throws ExecutionException("Some Failure", Exception())
assertThat(getTopicNames(), IsInstanceOf(Either.Left::class.java))
}
}

RxJava + Retrofit Unit Test Kotlin Always Failed

I tried to create Unit Test using Rxjava + Retrofit but it always give an error.
I have tried all tutorials and reference related of my questions. I did success when create an unit test of other method (other case), but failed in this case (Rx + retrofit).
Request Data Code:
fun getDetailEvent(idEvent: String?) {
view.showLoading()
apiService.getDetailEvent(idEvent)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
val compositeDisposable: CompositeDisposable? = null
compositeDisposable?.add(it)
}
.doFinally { view.hideLoading() }
.subscribe({
val listModel = it
if (listModel != null) {
view.onDetailEventLoaded(listModel)
} else {
view.onDetailEventLoadFailed("Empty or Error List")
}
},
{
val errorMessage = it.message
if (errorMessage != null) {
view.onDetailEventLoadFailed(errorMessage)
}
})
}
Unit Test Code :
class DetailNextMatchPresenterTest {
#Mock
private lateinit var view : DetailNextMatchView
#Mock
private lateinit var apiService: ApiService
private lateinit var presenter: DetailNextMatchPresenter
#Before
fun setup(){
MockitoAnnotations.initMocks(this)
presenter = DetailNextMatchPresenter(view, apiService)
}
#Test
fun getDetailEvent() {
val event : MutableList<EventModel> = mutableListOf()
val response = ResponseEventModel(event)
val idEvent = "44163"
`when`(apiService.getDetailEvent(idEvent)
.test()
.assertSubscribed()
.assertValue(response)
.assertComplete()
.assertNoErrors()
)
presenter.getDetailEvent(idEvent)
verify(view).showLoading()
verify(view).onDetailEventLoaded(response)
verify(view).hideLoading()
}
}
I appreciate all suggestion. Thanks
I believe that the issue is that you haven't forced your code to behave synchronously in the context of your test, so the Observable runs in parallel to your test. Try adding this in your setup method:
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() } If you're using RxJava2. Try looking for a similar method if you're using RxJava 1.