I want create MainCoroutineRule.
But TestCoroutineScope deprecated since 1.6.0.
How can I migrate it?
Below is my MainCoroutineRule
class MainCoroutineRule(
val dispatcher: TestCoroutineDispatcher =
TestCoroutineDispatcher()
): TestWatcher(), TestCoroutineScope by TestCoroutineScope(dispatcher) {
override fun starting(description: Description) {
super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description) {
super.finished(description)
cleanupTestCoroutines()
Dispatchers.resetMain()
}
}
also, I want to use it on JUnit5.
What should I do to migrate TestWatcher?
This is what I have so far, but I don't have a solution for cleanupTestCoroutines
class MainCoroutineRule(
private val dispatcher: TestDispatcher = StandardTestDispatcher()
) : TestWatcher() {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
}
}
Feel free to edit/modify this with a better solution
My solution will not be perfect, but it was my best.
According to migration tips, runTest does not support replacement for cleanupTestCoroutines().
Instead of that, they support better structured concurrency.
So you don't need to worry about cleanupTestCoroutines().
So, follow is my MainCoroutineExtension.
#OptIn(ExperimentalCoroutinesApi::class)
class MainCoroutineExtension(
private val testDispatcher: TestDispatcher = StandardTestDispatcher(),
val testScope: TestScope = TestScope(testDispatcher),
) : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext) {
Dispatchers.setMain(testDispatcher)
}
override fun afterEach(context: ExtensionContext) {
Dispatchers.resetMain()
}
}
#OptIn(ExperimentalCoroutinesApi::class)
fun MainCoroutineExtension.runTest(
block: suspend TestScope.() -> Unit
) = this.testScope.runTest {
block()
}
I use JUnit 5 and new coroutine.
So I couldn't use runBlockingTest because it will be deprecated soon.
If you have plan to migrate your coroutine, It will be helpful to you
Related
How can I (de)serialize kotlin delegate properties with jackson.
I have a class like this
class MyClass {
var a: Int = 42
set(value) {
val changed = field != value
field = value
if (changed) notifyListeners()
}
... and a dozen other properties that all follow this pattern ...
}
I wanted to simplify that by using
class MyClass {
var a: Int by NotifyUiOnChange(42)
...
private inner class NotifyUiOnChange<T>(initialValue: T) : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
notifyUiListeners()
}
}
}
but then Jackson will ignore that property.
How can I tell Jackson to serialize and deserialize that property anyway?
And how do I then apply #JsonIgnore annotations (or something comparable)?
You must use outdated version on Jackson (or maybe a version for Java, not Kotlin?). I've checked this using "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.+" (resolved to 2.10.1).
I've declared two classes:
class MyClass {
var a: Int = 42
set(value) {
val changed = field != value
field = value
if (changed) notifyListener(field)
}
private fun notifyListener(field: Any?) {
println("changed: $field")
}
}
class MyDelegatedClass {
var a: Int by NotifyUi(42)
private inner class NotifyUi<T>(initialValue: T) : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
notifyListener(newValue)
}
}
private fun notifyListener(field: Any?) {
println("changed: $field")
}
}
My main function:
fun main() {
val noDelegate = MyClass()
val delegated = MyDelegatedClass()
val mapper = ObjectMapper().registerKotlinModule()
// Deserialization
val noDelegateValue = mapper.writeValueAsString(noDelegate)
val delegatedValue = mapper.writeValueAsString(delegated)
println("No delegate:\t$noDelegateValue")
println("With delegate\t$delegatedValue")
// Serialization
val noDelegateObject = mapper.readValue<MyClass>("{\"a\":42}".trimIndent())
val delegateObject = mapper.readValue<MyDelegatedClass>("{\"a\":42}".trimIndent())
}
Output:
No delegate: {"a":42}
With delegate {"a":42}
changed: 42
We even can see output on delegate when we use delegate property :) (I believe it's a side-effect that should be consider as bug actually)
So, handling delegates is out of the box feature in jackson (I am not sure since when, but I used lazy delegate with jackson in older project I used to participate and there was no problems with delegates).
How to ignore delegated property?
So, you cannot apply JsonIgnore annotation to delegated field, because you will get This annotation is not applicable to target 'member property with delegate'. But, you can define the scope that annotation should be applied. Example below:
class MyDelegateClass {
#get:JsonIgnore // or set:
val a: Int by NotifyUi(42)
}
Unfortunately, seems that it's kind of broken, because you can use get: or set: and it's not apply to getter or setter only, but for both.
I am kind of new on Kotlin interfaces and abstract classes and similar stuff. I want to find a smart way to create a function in a DifferentActivity that returns an object with custom responses back in the MainActivity, like the following:
fun myFunction(): CustomObjectResponse {
try{
/* Heavy work */
return CustomObjectResponse.onFirstTypeSuccess(something)
}
catch(e: Exception){
return CustomObjectResponse.onFail(some_other_thing)
}
}
So in case of success, it returns one kind of response with a parameter, in case of failure, it returns a different response with a different parameter.
And then, in my MainActivity I want to implement the two different responses in something like:
DifferentActivity.myFunction().onResponse( object: CustomObjectResponse(){
override fun onFirstTypeSuccess(something: Any) {
// do stuff
}
override fun onFail(some_other_thing: Any) {
// do other stuff
}
}
Can something like this be done without extending / implementing anything on the MainActivity/DifferentActivity classes themselves, only limited at function level?
Thank you.
So ... you want something like this?
sealed class CustomObjectResponse
data class SuccessResponse(val x:X):CustomObjectResponse
data class FailResponse(val y:Y):CustomObjectResponse
fun myFunction(): CustomObjectResponse {
try{
/* Heavy work */
return SuccessResponse(something)
}
catch(e: Exception){
return FailResponse(some_other_thing)
}
}
and the MainActivity
fun handleResponse ( response: CustomObjectResponse ){
when(response){
is SuccessResponse -> {
println( response.x)
//and do stuff
}
is FailureResponse -> {
println( response.y)
//and do other stuff
}
}
}
??
How can we use kotlin.serialize with Ktor's HttpClient to deserialize/serialize JSON with lists as root? I am creating the HttpClient as follows:
HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer().apply {
setMapper(MyClass::class, MyClass.serializer())
setMapper(AnotherClass::class, AnotherClass.serializer())
}
}
install(ExpectSuccess)
}
Appears I need to setMapper for List, however that is not possible with generics. I see I can get the serializer for it with MyClass.serializer().list, but registering it to deserialize/serialize on http requests is not straight forward. Anyone know of a good solution?
You can write wrapper and custom serializer:
#Serializable
class MyClassList(
val items: List<MyClass>
) {
#Serializer(MyClassList::class)
companion object : KSerializer<MyClassList> {
override val descriptor = StringDescriptor.withName("MyClassList")
override fun serialize(output: Encoder, obj: MyClassList) {
MyClass.serializer().list.serialize(output, obj.items)
}
override fun deserialize(input: Decoder): MyClassList {
return MyClassList(MyClass.serializer().list.deserialize(input))
}
}
}
Register it:
HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer().apply {
setMapper(MyClassList::class, MyClassList.serializer())
}
}
}
And use:
suspend fun fetchItems(): List<MyClass> {
return client.get<MyClassList>(URL).items
}
Update with ktor 1.3.0:
Now you're able to receive default collections(such a list) from the client directly:
#Serializable
data class User(val id: Int)
val response: List<User> = client.get(...)
// or client.get<List<User>>(...)
Before ktor 1.3.0:
There is no way to (de)serialize such JSON in the kotlinx.serialization yet.
For serialization you could try something like this:
fun serializer(data: Any) = if (data is List<*>) {
if (data is EmptyList) String::class.serializer().list // any class with serializer
else data.first()::class.serializer().list
} else data.serializer()
And there are no known ways to get the list deserializer.
This is more of a workaround but after stepping through KotlinxSerializer code I couldn't see any other way round it. If you look at KotlinxSerializer.read() for example you can see it tries to look up a mapper based on type but in this case it's just a kotlin.collections.List and doesn't resolve. I had tried calling something like setListMapper(MyClass::class, MyClass.serializer()) but this only works for serialization (using by lookupSerializerByData method in write)
override suspend fun read(type: TypeInfo, response: HttpResponse): Any {
val mapper = lookupSerializerByType(type.type)
val text = response.readText()
#Suppress("UNCHECKED_CAST")
return json.parse(mapper as KSerializer<Any>, text)
}
So, what I ended up doing was something like (note the serializer().list call)
suspend fun fetchBusStops(): List<BusStop> {
val jsonArrayString = client.get<String> {
url("$baseUrl/stops.json")
}
return JSON.nonstrict.parse(BusStop.serializer().list, jsonArrayString)
}
Not ideal and obviously doesn't make use of JsonFeature.
I happened to have the same problem on Kotlin/JS, and managed to fix it this way:
private val client = HttpClient(Js) {
install(JsonFeature) {
serializer = KotlinxSerializer().apply {
register(User.serializer().list)
}
}
}
...
private suspend fun fetchUsers(): Sequence<User> =
client.get<List<User>> {
url("$baseUrl/users")
}.asSequence()
Hope this helps :)
I have a suspending functions that I have mocked, using Mockito but it is returning null
both projects use
'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
Example 1
here is my test in which the mock is returning null
#Test
fun `when gps not enabled observer is notified`() = runBlocking {
// arrange
`when`(suspendingLocationService.getCurrentLocation()).thenReturn(result) // <- when called this returns null
// act
presenter.onStartShopButtonClick()
// assert
verify(view).observer
verify(observer).onPrepareShop()
}
I have the below implementation in my presenter
override suspend fun onStartShopButtonClick() {
val result = suspendingLocationService.getCurrentLocation() // <- in my test result is null!!!!!!
view?.apply {
observer?.onPrepareShop()
when {
result.hasGivenPermission == false -> observer?.onStartShop(StoreData(), APIError(APIError.ErrorType.NO_PERMISSION))
result.hasGPSEnabled == false -> observer?.onStartShop(StoreData(), APIError(APIError.ErrorType.GPS_NOT_ENABLED))
result.latitude != null && result.longitude != null ->
storeLocationService.getCurrentStore(result.latitude, result.longitude) { store, error ->
observer?.onStartShop(store, error)
}
}
}
}
however I have what I believe to a very similar implementation that is working below
Example 2
The below test does pass and the correct the function does respond with a product
#Test
fun `suspending implementation updates label`() = runBlocking {
// arrange
`when`(suspendingProductProvider.getProduct("testString")).thenReturn(product)
// act
presenter.textChanged("testString")
// assert
verify(view).update(product.name)
}
here is the implementation of the presenter
override suspend fun textChanged(newText: String?) {
val product = suspendingNetworkProvider.getProduct(newText)
view?.update(product.name)
}
here is the interface I am mocking
interface SuspendingProductProvider {
suspend fun getProduct(search: String?): Product
}
what I am not doing in the first example
Mockito has a special support for suspend functions, but in Kotlin 1.3 there were some changes in how coroutines are implemented internally, so older versions of Mockito are no longer recognize suspend methods compiled by Kotlin 1.3. And kotlinx.coroutines use Kotlin 1.3 since version 1.0.0.
Corresponding support was added to Mockito, but only since version 2.23, so updating your Mockito version will help.
first get Mockito-kotlin and
in mockito you can use this code when you want to mock suspend functions :
val mockedObject: TestClass = mock()
mockedObject.stub {
onBlocking { suspendFunction() }.doReturn(true)
}
You can mock multiple functions during initialisation. Let say, we have repository:
interface ContactRepository {
suspend fun getContact(contactId: Long): Contact
fun getContactsFlow(): Flow<List<Contact>>
}
You can mock both method in place:
val testContact = ContactModel(testId)
val repository: ContactRepository = mock {
onBlocking { getContact(testId) } doReturn testContact
on { getContactsFlow() } doReturnFlow flowOf(listOf(testContact))
}
The top answer is the correct answer. I upgraded to mockito 2.23 and was able to do this successfully, without encountering the issue of null value. I faced the same issue with mockito 2.21
class Parser {
suspend fun parse(responseBody: ByteArray) : Result = coroutineScope {/*etc*/}
}
val expectedResult = Mockito.mock(Result::class.java)
Mockito.`when`(mockParser.parse(byteArrayOf(0,1,2,3,4))).thenReturn(coroutineScope {
expectedResult
})
currently I'm designing a domain model for an application. I created a simple value object that's basically just a wrapper around a string enhanced with some business logic.
Now the default behaviour of jackson is to render the object like
"routerId": {
"routerId": "aa:aa:aa:aa:aa:aa"
}
for
#Embeddable
data class RouterId(val routerId: String) {
init {
val octets = routerId.split(":")
if (octets.size != 6) {
throw IllegalArgumentException("$routerId does not consist of 6 octets")
}
for (octet in octets) {
Integer.parseInt(octet, 16)
}
}
}
I stumbeld accross http://docs.spring.io/spring-data/rest/docs/2.6.3.RELEASE/reference/html/#_adding_custom_de_serializers_to_jackson_s_objectmapper and tried to supply my custom jackson module to handle serialization with
class NicemediaModule : SimpleModule("NicemediaModule") {
override fun setupModule(context: SetupContext?) {
val serializers = SimpleSerializers()
serializers.addSerializer(RouterId::class.java, RouterIdSerializer())
context?.addSerializers(serializers)
}
}
private class RouterIdSerializer : StdSerializer<RouterId>(RouterId::class.java) {
override fun serialize(value: RouterId?, gen: JsonGenerator?, provider: SerializerProvider?) {
gen?.writeString(value?.routerId)
}
}
and
#Configuration
open class SpringDataRestConfiguration : RepositoryRestConfigurerAdapter() {
override fun configureJacksonObjectMapper(objectMapper: ObjectMapper?) {
objectMapper?.registerModule(NicemediaModule())
}
}
but this only leads to
"routerId": {
"content": "aa:aa:aa:aa:aa:aa"
}
Could anyone point out what I would have to do to serialize the RouterId just to a plain string like "routerId": "aa:aa:aa:aa:aa:aa"?
Edit:
I added #Component to my SimpleModule so that Spring Boot loads it by default and wrote a litte test to see if the ObjectMapper works.
#SpringBootTest
#RunWith(SpringRunner::class)
class JsonSerializationTest {
#Autowired
private lateinit var mapper: ObjectMapper
#Test
fun serializeRouterId() {
val routerId: String = "11:11:11:11:11:11"
assertEquals("\"$routerId\"", mapper.writeValueAsString(RouterId(routerId)))
}
}
works quite fine. This may be an indicator that my code is working the whole time but Spring Data REST fails to serialize my model at some point.
Try implementing a custom BackendIdConverter SPI as suggested in this answer. Works like a charm!