Mockito when clause not working in kotlin - kotlin

I am trying to use mockito in kotlin. I created the following test:
class HeroesDataSourceTest {
#Mock
lateinit var heroesRepository: HeroesRepository
#Mock
lateinit var params: PageKeyedDataSource.LoadInitialParams<Int>
#Mock
lateinit var callback: PageKeyedDataSource.LoadInitialCallback<Int, Heroes.Hero>
val hero = Heroes.Hero(1, "superman", "holasuperman", 1, null, null)
val results = Arrays.asList(hero)
val data = Heroes.Data(results)
val dataResult = Heroes.DataResult(data)
val compositeDisposable = CompositeDisposable()
lateinit var heroesDataSource: HeroesDataSource
private val heroesPublishSubject = PublishSubject.create<Heroes.DataResult>()
#Before
fun initTest(){
MockitoAnnotations.initMocks(this)
}
#Test
fun testLoadInitialSuccess(){
`when`(heroesRepository.getHeroes(ArgumentMatchers.anyInt())).thenReturn(heroesPublishSubject.singleOrError())
heroesDataSource = HeroesDataSource(heroesRepository, compositeDisposable)
val testObserver = TestObserver<Heroes.DataResult>()
heroesDataSource.loadInitial(params, callback)
heroesPublishSubject.onNext(dataResult)
testObserver.assertComplete()
}
}
But when I execute it in the line when(heroesRepository.getHeroes(ArgumentMatchers.anyInt())).thenReturn(heroesPublishSubject.singleOrError()) it just enter to getHeroes method instead of mocking it (and for sure since heroesRepository is not initialized because is mocket the method fails). I use this tons of times in java and it never gave me a single problem. What I have to do in kotlin to mock it properly?
EDIT
Here I put also HeroesRepository class
open class HeroesRepository {
val privateKey = "5009bb73066f50f127907511e70f691cd3f2bb2c"
val publicKey = "51ef4d355f513641b490a80d32503852"
val apiDataSource = DataModule.create()
val pageSize = 20
fun getHeroes(page: Int): Single<Heroes.DataResult> {
val now = Date().time.toString()
val hash = generateHash(now + privateKey + publicKey)
val offset: Int = page * pageSize
return apiDataSource.getHeroes(now, publicKey, hash, offset, pageSize)
}
fun generateHash(variable: String): String {
val md = MessageDigest.getInstance("MD5")
val digested = md.digest(variable.toByteArray())
return digested.joinToString("") {
String.format("%02x", it)
}
}
}

Without adding another dependency, you can replace using the #Mock annotation with a helper function somewhere:
inline fun <reified T> mock(): T =
Mockito.mock(T::class.java)
// To avoid having to use backticks for "when"
fun <T> whenever(methodCall: T): OngoingStubbing<T> =
Mockito.`when`(methodCall)
Then in your test:
val heroesRepository: HeroesRepository = mock()
#Test
fun yourTest() {
whenever(heroesRepository.getHeroes(ArgumentMatchers.anyInt()))
.thenReturn(heroesPublishSubject.singleOrError())
}
Like you had before. This should work, because you're not expecting Mockito to deal with #Mock lateinit var, which it seems to be struggling with, and instead instantiating the mock yourself.

FWIW, the reason that the when statement is just calling the actual function is because the function itself (getHeroes) is not marked as open.
So even though the class is non-final the method is final and not mocked.

Mockito is not quite compatible with kotlin, you can use mockito-kotlin resource instead.
Check out this reference:
https://github.com/nhaarman/mockito-kotlin
You can easily mock objects like so:
val heroesRepository = mock<HeroesRepository>()
Then you can use it as you use mockito

Related

How to initialize lateinit var with 'by keyword' in kotlin

I want to initialize lateinit variable with 'by'.
How can I?
lateinit var test: int
fun main() {
test by something {} // error!
}
I tried using by in by lazy, and I tried using by in lateinit var, but it didn't work.
You don't need lateinit when using by lazy. Lazy means it'll be initialized the first time it's referenced. lateinit means you manually assign a value some time after construction.
So all you need is
val test by lazy { something() }
fun main() {
println(test) // runs the initializer and prints the value
}
Update:
Or, if you want to initialize an exising lateinit property:
lateinit var test: Type
fun main() {
val someting by other
test = something
}

Kotlin Abstract Val Is Null When Accessed In Init Before Override

In Kotlin, accessing an abstract val in an init block causes a NullPointerException since the field is overridden by an extending class after the super class's init block executes.
The ideal solution would be a way to declare some code/function to execute after all stages of object instantiation are complete. I can only think of creating an initialize() function and manually calling it, which is bad because it's not automatic. Sticking it in init block doesn't work as shown in the below example.
As a comment pointed out below, instead of overriding fields, they can be passed in as parameters, but that doesn't work for my actual use-case. It adds a lot of clutter for object construction and is a nightmare when other classes try to extend it.
Below example shows a solution using coroutines. Waiting for a field to != null works in this case, but doesn't not when map is an open val with a default value that may or may not get overridden.
The problem is somewhat solved, but the solution is far from optimal. Any suggestions and alternative solutions would be greatly appreciated.
#Test #Suppress("ControlFlowWithEmptyBody", "SENSELESS_COMPARISON")
fun abstractValAccessInInitNPE() {
val key = "Key"
val value = "Value"
abstract class Mapper {
abstract val map: HashMap<String, String>
fun initialize() { map[key] = value }
}
// Test coroutine solution on abstract mapper
println("CoroutineMapper")
abstract class CoroutineMapper: Mapper() {
init {
GlobalScope.launch {
while (map == null) {}
initialize()
}
}
}
val coroutineMapper = object : CoroutineMapper() {
override val map = HashMap<String, String>()
}
val start = System.nanoTime()
while (coroutineMapper.map.isEmpty()) {} // For some reason map == null doesn't work
println("Overhead: ${(System.nanoTime() - start) / 1000000.0} MS")
println("Mapped: ${coroutineMapper.map[key].equals(value)}")
// Test coroutine solution on open mapper
println("\nDefaultMapper")
open class DefaultMapper: Mapper() {
override val map = HashMap<String, String>()
}
val newMap = HashMap<String, String>()
val proof = "Proof"
newMap[proof] = proof
val defaultMapper = object: DefaultMapper() {
override val map = newMap
}
Thread.sleep(1000) // Definitely finished by the end of this
println("Mapped: ${defaultMapper.map[proof].equals(proof) && defaultMapper.map[key].equals(value)}")
// Basic solution (doesn't work)
println("\nBrokenMapper")
abstract class BrokenMapper: Mapper() {
init { initialize() } // Throws NPE because map gets overridden after this
}
val brokenMapper = object: BrokenMapper() {
override val map = HashMap<String, String>()
}
println("Mapped: ${brokenMapper.map[key].equals(value)}")
}
An open (as all abstract functions are) function should never be called from a constructor because then the class's initial state cannot be guaranteed in the superclass. It can lead to all kinds of very tricky bugs.
Usually there's a good way to design around this problem if you take a step back. For instance, instead of making the map an abstract property, make it a constructor parameter in the superclass. Then you know it's already initialized before subclass constructors can try to use it.
abstract class Mapper(key: String, value: String, val map: HashMap<String, String>)
abstract class DecentMapper(key: String, value: String, map: HashMap<String, String>) : Mapper(key, value, map) {
init {
map[key] = value
}
}
val key = "Key"
val value = "Value"
val decentMapper = object : DecentMapper(key, value, HashMap()){
//...
}

Kotlin Generics, correct syntax for type parameters

I have the following class, which basically gets a JSON string from AWS, then converts it to an instance of a data class...
class SecretsManager(region: String) {
private val gson = Gson()
private val smClient = AWSSecretsManagerClientBuilder.standard().withRegion(region).build()
fun <T> getSecret(id: String): T {
val req = GetSecretValueRequest().withSecretId(id)
val json = smClient.getSecretValue(req).getSecretString()
return gson.fromJson(json, T::class.java)
}
}
To be used like this...
val myInstance = SecretsManager("eu-west-2").getSecret<MyDataClass>("myId")
Currently, I get an error - Cannot use 'T' as reified type parameter. I can get around this by marking the function as inline and T as reified , but then I can't access the private attributes from within the function.
What's the best way to do this in Kotlin?
You need to add another parameter to the getSecret method, and also need to add an inline reified method for that to work. See the code below
class SecretsManager(region: String) {
private val gson = Gson()
private val smClient = AWSSecretsManagerClientBuilder.standard().withRegion(region).build()
fun <T> getSecret(type: Class<T>, id: String): T {
val req = GetSecretValueRequest().withSecretId(id)
val json = smClient.getSecretValue(req).getSecretString()
return gson.fromJson(json, type)
}
inline fun <reified T> getSecret(id: String): T = getSecret(T::class.java, id)
}

how to make mockito test case with Constructor injection

I want to make junit test case with mockito
there are Classes.
MyProps : the properties data class #Configuration
MyService : my main logic #Service class
MyClient : webClient class #Component
how to test myService result logic with mockito?? and how to make data class(MyProps) simply???
there error is like this :
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Parameter specified as non-null is null:
#Configuration
#ConfigurationProperties("application.something")
class MyProps {
lateinit var broker: String
lateinit var url: String
}
#Service
class MyService(private val client: MyClient,
private val myProps: MyProps
){
fun getReulst(params: Params): Flux<MyEntity> {
// some logic, and I want check this part result
return client.get(params)
}
}
#Component
class MyClient(val myProps: MyProps) {
private val webClient: WebClient = WebClient.create()
fun get(params: Params): Flux<MyEntity> {
val params = BodyInserters.fromValue(query)
return webClient
.post()
.uri(myProps.broker)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(params)
.retrieve()
.bodyToFlux(MyEntity::class.java)
}
}
here is test code
//----------------
#SpringBootTest(classes = [MyService::class, MyProps::class, MyClient::class])
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyServiceTest {
#MockBean
private lateinit var myProps: MyProps
#MockBean
private lateinit var myClient: MyClient
#InjectMocks
private lateinit var countService: MyService
#BeforeEach
fun mock() {
given(myProps.broker).willReturn("http://url.com")
given(myProps.web).willReturn("something")
given(myClient.get2(query)).willReturn(
Flux.fromIterable(listOf(
MyEntity("2020-03-22", Count(1)),
MyEntity("2020-03-23", Count(2)),
MyEntity("2020-03-24", Count(3)),
MyEntity("2020-03-25", Count(6)),
MyEntity("2020-03-26", Count(5)),
MyEntity("2020-03-27", Count(4))
))
)
MockitoAnnotations.initMocks(this)
}
#Test
fun `I want to check!! here`(){
val param = Params(1,2,3) // some params
myService.getReulst(param).subscribe() // <- here is error maybe param is null... why??
}
}
Since you're injecting mocks into your service you don't really need a full-blown spring boot test. This will make your tests faster.
Furthermore, since you're mocking MyClient, you're not really calling its code. Therefore whatever happens in MyClient can be ignored here. You'll cover it in its own test.
Here's how I'd write this test with JUnit 5 (you'll have to translate it to JUnit 4 if you're still on it):
#ExtendWith(MockitoExtension::class)
class MyServiceTest(
#Mock val myProps: MyProps,
#Mock val myClient: MyClient
) {
// you can use #InjectMocks if you prefer
val countService: MyService = MyService(myProps, myClient)
#BeforeEach
fun mock() {
// you only need these if they're called in MyService
`when`(myProps.broker).thenReturn("http://url.com")
`when`(myProps.web).thenReturn("something")
`when`(myClient.get(Params(1,2,3))).thenReturn(
Flux.fromIterable(listOf(
MyEntity("2020-03-22", Count(1)),
MyEntity("2020-03-23", Count(2)),
MyEntity("2020-03-24", Count(3)),
MyEntity("2020-03-25", Count(6)),
MyEntity("2020-03-26", Count(5)),
MyEntity("2020-03-27", Count(4))
))
)
}
#Test
fun `I want to check!! here`(){
val param = Params(1,2,3)
myService.getReulst(param).subscribe()
}
}

Delegate property to another property

Can you delegate a property to another property in Kotlin? I have the following code:
class SettingsPage {
lateinit var tagCharacters: JTextField
lateinit var tagForegroundColorChooser: ColorPanel
lateinit var tagBackgroundColorChooser: ColorPanel
var allowedChars: String
get() = tagCharacters.text
set(value) = tagCharacters.setText(value)
var tagForegroundColor by tagForegroundColorChooser
var tagBackgroundColor by tagBackgroundColorChooser
}
In order to get property delegation, I declare the following two extension functions:
operator fun ColorPanel.getValue(a: SettingsPage, p: KProperty<*>) = selectedColor
operator fun ColorPanel.setValue(a: SettingsPage, p: KProperty<*>, c: Color?) { selectedColor = c }
However, what I would like to write is something like the following:
class SettingsPage {
lateinit var tagCharacters: JTextField
lateinit var tagForegroundColorChooser: ColorPanel
lateinit var tagBackgroundColorChooser: ColorPanel
var allowedChars: String by Alias(tagCharacters.text)
var tagForegroundColor by Alias(tagForegroundColorChooser.selectedColor)
var tagBackgroundColor by Alias(tagBackgroundColorChooser.selectedColor)
}
Is this possible to do Kotlin? How do I write the class Alias?
UPD: Since Kotlin 1.4, the standard library includes the necessary extensions that allow this out of the box:
class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
var delegatedToMember: Int by this::memberInt
var delegatedToTopLevel: Int by ::topLevelInt
val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
}
var MyClass.extDelegated: Int by ::topLevelInt
Yes, it's possible: you can use a bound callable reference for a property that you store in the alias, and then the Alias implementation will look like this:
class Alias<T>(val delegate: KMutableProperty0<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T =
delegate.get()
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
delegate.set(value)
}
}
And the usage:
class Container(var x: Int)
class Foo {
var container = Container(1)
var x by Alias(container::x)
}
To reference a property of the same instance, use this::someProperty.
Follow-up, if you would like to see support for property references in Kotlin, please vote and track this issue for updates: https://youtrack.jetbrains.com/issue/KT-8658