Vertx Instance not initialized when using Kotlin Coroutines - kotlin

I am trying to writer an HTTP server using a Vert.x CoroutineVerticle, but I get an error lateinit property vertxInstance has not been initialized. It seems like the init(...) function of CoroutineVerticle is not being called when the verticle is deployed.
package http
import io.vertx.core.DeploymentOptions
import io.vertx.core.Vertx
import io.vertx.ext.web.Router
import io.vertx.kotlin.core.http.listenAwait
import io.vertx.kotlin.coroutines.CoroutineVerticle
class CoTest : CoroutineVerticle() {
private var router = createRouter()
override suspend fun start() {
vertx.createHttpServer()
.requestHandler(router)
.listenAwait(config.getInteger("http.port", 8182))
}
private fun createRouter() = Router.router(vertx).apply {
get("/favicon.ico").handler { req ->
req.response().end("NO")
}
}
}
fun main() {
class V : ServerVertxCo(8182) {}
Vertx.vertx().deployVerticle(CoTest::class.java.canonicalName)
}

The problem is the order you do things in your class.
The order for vertx is:
Create the vertx instance
Deploy a verticle
Verticle class constructor is called (vertx is not "injected" to it yet)
Vertx does some internal initializations
Vertx calls the verticle's start function
You use private var router = createRouter() in the class (outside the start block) and at that point vertx is not "injected" yet to the class so you get an exception.
If you move it to the start block it should work as you expect:
class CoTest : CoroutineVerticle() {
override suspend fun start() {
val router = createRouter()
vertx.createHttpServer()
.requestHandler(router)
.listenAwait(config.getInteger("http.port", 8182))
}
private fun createRouter() = Router.router(vertx).apply {
get("/favicon.ico").handler { req ->
req.response().end("NO")
}
}
}
suspend fun main() {
val vertx = Vertx.vertx()
vertx.deployVerticleAwait(CoTest::class.java.canonicalName)
}

Related

Mock class used in a method Kotlin

I am using mockk Kotlin library. I have a service Service that has a private method calling a 3d party client
class Service {
fun foo() {
bar()
}
private fun bar() {
client = Client()
client.doStuff()
}
}
Now in my test I need to mock Client, e.g.
#Test
fun `my func does what I expect` {
}
I need to also mock what doStuff returns. How do I achieve this in Kotlin mockk?
Firstly, you should never instantiate a dependency like Client inside your service class since you cannot access it to provide a Mock. Let's deal with that first...
class Client { // this is the real client
fun doStuff(): Int {
// access external system/file/etc
return 777
}
}
class Service(private val client: Client) {
fun foo() {
bar()
}
private fun bar() {
println(client.doStuff())
}
}
and then this how to use Mockk
class ServiceTest {
private val client: Client = mockk()
#Test
fun `my func does what I expect`() {
every { client.doStuff() } returns 666
val service = Service(client)
service.foo()
}
}

Building non-blocking VertX server with CoroutineVerticle

I'm experimenting with VertX+Couroutines and just want to check if this setup is blocking at any point or has potential issues that i need to be aware of.
For example, is runBlocking being used correctly in this instance or should i rather do a deployVerticle? And then inside requestHandler, i'm doing GlobalScope.launch, this seems to be discouraged, what is the correct scope to use here?
I've added VertX 4.0.0-milestone5 to my Gradle build script, i'm not using VertX Web:
val vertxVersion = "4.0.0-milestone5"
implementation("io.vertx:vertx-core:$vertxVersion") {
exclude(group = "com.fasterxml.jackson.core", module = "jackson-core")
exclude(group = "com.fasterxml.jackson.core", module = "jackson-databind")
exclude(group = "log4j", module = "log4j")
exclude(group = "org.apache.logging.log4j", module = "log4j-api")
exclude(group = "org.apache.logging.log4j", module = "log4j-core")
}
implementation("io.vertx:vertx-lang-kotlin:$vertxVersion")
implementation("io.vertx:vertx-lang-kotlin-coroutines:$vertxVersion")
Inside Routing.kt i have the following setup:
class Routing(
private val port: Int
) : CoroutineVerticle() {
override suspend fun start() {
Vertx.vertx().createHttpServer(
HttpServerOptions().setCompressionSupported(true)
).requestHandler { req ->
GlobalScope.launch {
try {
log.info("${req.method()}:${req.path()}")
req.response().setStatusCode(200).end("Hello World")
} catch (e: Exception) {
log.error(e.message ?: "", e)
req.response().setStatusCode(500).end("Something Went Wrong")
}
}
}.listen(port)
log.info("Listening on $port")
}
override suspend fun stop() {
}
companion object {
private val log = LoggerFactory.getLogger(Routing::class.java)
private val root = RoutingTree()
suspend fun setup(port: Int) {
Endpoint.all.forEach {
root.addPath(it.key, it.value)
}
log.info("\n" + root.toString())
Routing(port = port).start()
}
}
}
This Routing.setup is then used inside main()
object Server {
private val log = LoggerFactory.getLogger(this.javaClass)
#JvmStatic
#ExperimentalTime
fun main(args: Array<String>) = runBlocking {
....
// setup routing
Routing.setup(
port = if (ENV.env == LOCAL) {
5555
} else {
80
},
)
The whole point of Kotlin integration with Vert.x is that you don't have to use GlobalScope.launch
Here's a minimal example of how it can be achieved:
fun main() {
val vertx = Vertx.vertx()
vertx.deployVerticle("Server")
}
class Server : CoroutineVerticle() {
override suspend fun start() {
vertx.createHttpServer().requestHandler { req ->
// You already have access to all coroutine generators
launch {
// In this scope you can use suspending functions
delay(1000)
req.response().end("Done!")
}
}.listen(8888)
}
}

Listening to coroutine from view cant be done from the views init

I am trying to listen to my ViewModels MutableStateFlow from my FlutterSceneView. But I get the following error when trying to set the listener from the views init:
Suspend function 'listenToBackgroundColor' should be called only from a coroutine or another suspend function
class FlutterSceneView(context: Context, private val viewModel: FlutterSceneViewModelType): PlatformView {
private val context = context
private val sceneView = SceneView(context)
init {
listenToBackgroundColor() // Error here
}
private suspend fun listenToBackgroundColor() {
viewModel.colorFlow.collect {
val newColor = Color.parseColor(it)
sceneView.setBackgroundColor(newColor)
}
}
}
My ViewModel:
interface FlutterSceneViewModelType {
var colorFlow: MutableStateFlow<String>
}
class FlutterSceneViewModel(private val database: Database): FlutterSceneViewModelType, ViewModel() {
override var colorFlow = MutableStateFlow<String>("#FFFFFF")
init {
listenToBackgroundColorFlow()
}
private fun listenToBackgroundColorFlow() {
database.backgroundColorFlow.watch {
colorFlow.value = it.hex
}
}
}
the .watch call is a helper I have added so that this can be exposed to iOS using Kotlin multi-platform, it looks as follows but I can use collect instead if necessary:
fun <T> Flow<T>.asCommonFlow(): CommonFlow<T> = CommonFlow(this)
class CommonFlow<T>(private val origin: Flow<T>) : Flow<T> by origin {
fun watch(block: (T) -> Unit): Closeable {
val job = Job()
onEach {
block(it)
}.launchIn(CoroutineScope(Dispatchers.Main + job))
return object : Closeable {
override fun close() {
job.cancel()
}
}
}
}
I resolved this by using viewModel context:
private fun listenToBackgroundColor() {
viewModel.colorFlow.onEach {
val newColor = Color.parseColor(it)
sceneView.setBackgroundColor(newColor)
}.launchIn(viewModel.viewModelScope)
}
I had to import the following into my ViewModel:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
from:
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")

kotlin flow is not emitting values from different function

I am trying to implement kotlin stateflow, but not able to know the reason why it is not working.
Current output:
verificatio 34567
Expected Output:
verificatio 34567
verificatio failed
package stateflowDuplicate
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val firebasePhoneVerificationListener = FirebaseOTPVerificationOperation1()
val oTPVerificationViewModal = OTPVerificationViewModal1(firebasePhoneVerificationListener)
oTPVerificationViewModal.fail()
}
class OTPVerificationViewModal1(private val firebasePhoneVerificationListener: FirebaseOTPVerificationOperation1) {
init {
startPhoneNumberVerification()
setUpListener()
}
suspend fun fail(){
firebasePhoneVerificationListener.fail()
}
private fun startPhoneNumberVerification() {
firebasePhoneVerificationListener.initiatePhoneVerification("34567")
}
private fun setUpListener() {
runBlocking {
firebasePhoneVerificationListener.phoneVerificationFailed.collect {
println("verificatio $it")
}
}
}
}
Second class
package stateflowDuplicate
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.runBlocking
class FirebaseOTPVerificationOperation1 {
private val _phoneVerificationFailed = MutableStateFlow<String?>(null)
val phoneVerificationFailed: StateFlow<String?>
get() = _phoneVerificationFailed
fun initiatePhoneVerification(phoneNumber: String) {
_phoneVerificationFailed.value = phoneNumber
}
suspend fun fail() {
delay(200L)
_phoneVerificationFailed.value = "failed"
}
}
Tried to understand the concept from these links,
Link1
Link2
You have to start a new coroutine to call collect because the coroutine will keep collecting values until its Job gets cancelled. Don't use runBlocking builder for that, use launch builder instead:
private fun setUpListener() = launch {
firebasePhoneVerificationListener.phoneVerificationFailed.collect {
println("verificatio $it")
}
}
Now to make it work you need to implement CoroutineScope interface in your class. You can do it like this:
class OTPVerificationViewModal1(
private val firebasePhoneVerificationListener: FirebaseOTPVerificationOperation1
): CoroutineScope by CoroutineScope(Dispatchers.Default) {
...
}
If you run it now you will get this output:
verificatio 34567
verificatio failed

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)
}
}