Given
typealias MyCallback = (s: String) -> Unit
object Hello {
fun main() {
blah { print(it) }
}
fun blah(cb: MyCallback) {
cb.invoke("hi")
}
}
Or
interface MyCallback {
fun invoke (s: String) {}
}
object Hello {
fun main() {
blah(object : MyCallback {
override fun invoke(s: String) {
print(s)
}
})
}
fun blah(cb: MyCallback) {
cb.invoke("hi")
}
}
With both I get the above error (Verification failed: call 1 of 1: MyCallback(#2).invoke(any())) was not called) when doing
#Test
fun testInvoke() {
val mock = mockk<Hello>(relaxed = true)
val cb = mockk<MyCallback>()
mock.blah(cb)
verify { cb.invoke(any()) }
}
how to fix it?
This worked for me. The Hello object doesn't need to be mocked since it is the class being tested. By mocking it, the test was only recording invocations of blah() rather than actually executing them.
Using spyk rather than mockk allows the MyCallback type to be constructed allowing the invoke() function to be defined. So maybe that's more of a workaround than an explanation of why mockk doesn't seem to retain that type info.
typealias MyCallback = (s: String) -> Unit
object Hello {
fun main() {
blah { print(it) }
}
fun blah(cb: MyCallback) {
cb.invoke("hi")
}
}
class MockKTest {
#Test
fun testInvoke() {
val mock = spyk<Hello>()
val cb = mockk<MyCallback>(relaxed = true)
mock.blah(cb) // or just do Hello.blah(cb)
verify { cb.invoke(any()) }
}
}
Related
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
https://pl.kotl.in/WJxo0DujU (below is the code in the link)
open class A() { }
class B(): A() {
fun pew2() { }
}
fun <a: A> a.pew() = apply { }
fun main() {
val b = B()
b.pew().pew2()
}
Is there a way to have the function pew() in class A (not in A's companion object) and still be able to type b.pew().pew2() (not b.apply { pew().pew2() })?
You could create the pew() method in A and have the method return the instance itself:
open class A() {
fun pew() = this
}
class B(): A() {
fun pew2() { }
}
fun main() {
val b = (B().pew() as B).pew2()
}
You can override the function in B and narrow the return type.
open class A() {
open fun pew(): A {
//...
return this
}
}
class B: A() {
fun pew2() { }
override fun pew(): B {
super.pew()
return this
}
}
Kotlin doesn't have a self type that would make these kinds of chainable functions easier to implement, but the apply function makes chainable functions that return the same object unnecessary.
I am creating my first kotlin multiplataform project, and i'm having some difficulty to use the kotlin flow on swift. I created the models, server data requests as common file using kotlin flow and ktor, the the view model and ui layers i am creating as native. So, i have no experience with swift development, and beyond that i' having a lot of trouble to use flow on swift view model. Searching for an answer to my problem i found a class described as CommonFlow, which aims to be used as a common code for both languages(kotlin, swift, but i'm having an error that gives me little or no clue as to why it happens or, probably, it's just my lack of dominion with xcode and swift programming:
So this is the code part that xcode points the error:
Obs: sorry about the image i thought that maybe this time it would be more descriptive
And this its all i got from the error
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee5928ff8)
MY Ios ViewModel:
class ProfileViewModel: ObservableObject {
private let repository: ProfileRepository
init(repository: ProfileRepository) {
self.repository = repository
}
#Published var authentication: Authetication = Authetication.unauthenticated(false)
#Published var TokenResponse: ResponseDTO<Token>? = nil
#Published var loading: Bool = false
func authenticate(email: String, password: String) {
DispatchQueue.main.async {
if(self.isValidForm(email: email, password: password)){
self.repository.getTokenCFlow(email: email, password: password).watch{ response in
switch response?.status {
case StatusDTO.success:
self.loading = false
let token: String = response!.data!.accessToken!
SecretStorage().saveToken(token: token)
self.authentication = Authetication.authenticated
break;
case StatusDTO.loading:
self.loading = true
break;
case StatusDTO.error:
print("Ninja request error \(String(describing: response!.error!))}")
break;
default:
break
}
}
}
}
}
private func isValidForm(email: String, password: String) -> Bool {
var invalidFields = [Pair]()
if(!isValidEmail(email)){
invalidFields.append(Pair(first:"email invalido",second: "email invalido"))
}
if(password.isEmpty) {
invalidFields.append(Pair(first:"senha invalida",second: "senha invalida"))
}
if(!invalidFields.isEmpty){
self.authentication = Authetication.invalidAuthentication(invalidFields)
return false
}
return true
}
private func isValidEmail(_ email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
}
class Pair {
let first: String
let second: String
init(first:String, second: String) {
self.first = first
self.second = second
}
}
enum Authetication {
case invalidAuthentication([Pair])
case authenticated
case persistentAuthentication
case unauthenticated(Bool)
case authenticationFailed(String)
}
The repository methods:
override fun getToken(email: String, password: String): Flow<ResponseDTO<Token>> = flow {
emit(ResponseDTO.loading<Token>())
try {
val result = api.getToken(GetTokenBody(email, password))
emit(ResponseDTO.success(result))
} catch (e: Exception) {
emit(ResponseDTO.error<Token>(e))
}
}
#InternalCoroutinesApi
override fun getTokenCFlow(email: String, password: String): CFlow<ResponseDTO<Token>> {
return wrapSwift(getToken(email, password))
}
The Class CFLOW:
#InternalCoroutinesApi
class CFlow<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()
}
}
}
}
#FlowPreview
#ExperimentalCoroutinesApi
#InternalCoroutinesApi
fun <T> ConflatedBroadcastChannel<T>.wrap(): CFlow<T> = CFlow(asFlow())
#InternalCoroutinesApi
fun <T> Flow<T>.wrap(): CFlow<T> = CFlow(this)
#InternalCoroutinesApi
fun <T> wrapSwift(flow: Flow<T>): CFlow<T> = CFlow(flow)
There is an example of using flows in KampKit
https://github.com/touchlab/KaMPKit
I will paste an excerpt from NativeViewModel (iOS)
class NativeViewModel(
private val onLoading: () -> Unit,
private val onSuccess: (ItemDataSummary) -> Unit,
private val onError: (String) -> Unit,
private val onEmpty: () -> Unit
) : KoinComponent {
private val log: Kermit by inject { parametersOf("BreedModel") }
private val scope = MainScope(Dispatchers.Main, log)
private val breedModel: BreedModel = BreedModel()
private val _breedStateFlow: MutableStateFlow<DataState<ItemDataSummary>> = MutableStateFlow(
DataState.Loading
)
init {
ensureNeverFrozen()
observeBreeds()
}
#OptIn(FlowPreview::class)
fun observeBreeds() {
scope.launch {
log.v { "getBreeds: Collecting Things" }
flowOf(
breedModel.refreshBreedsIfStale(true),
breedModel.getBreedsFromCache()
).flattenMerge().collect { dataState ->
_breedStateFlow.value = dataState
}
}
This ViewModel is consumed in swift like this:
lazy var adapter: NativeViewModel = NativeViewModel(
onLoading: { /* Loading spinner is shown automatically on iOS */
[weak self] in
guard let self = self else { return }
if (!(self.refreshControl.isRefreshing)) {
self.refreshControl.beginRefreshing()
}
},
onSuccess: {
[weak self] summary in self?.viewUpdateSuccess(for: summary)
self?.refreshControl.endRefreshing()
},
onError: { [weak self] error in self?.errorUpdate(for: error)
self?.refreshControl.endRefreshing()
},
onEmpty: { /* Show "No doggos found!" message */
[weak self] in self?.refreshControl.endRefreshing()
}
)
In short, the flow is kept wrapped in kotlin mp land and leveraged in iOS by using traditional callback interfaces.
class ModelFactory {
fun setA() : ModelFactory {
// blabla...
}
fun setB() : ModelFactory {
// blabla...
}
fun setC() : ModelFactory {
// blabla...
}
fun build() : Model {
// An error occurs if any of setA, setB, and setC is not called.
}
}
//example
fun successTest() {
ModelFactory().setA().setB().setC().build() // No error occurs at compile time
}
fun failTest() {
ModelFactory().setA().build() // An error occurs at compile time because setB and setC are not called.
}
It's awkward grammatically, but I think it's been expressed what I want.
I have already implemented an error-raising runtime for this requirement, but I want to check this at compile time.
If possible, I think I should use annotations. But is this really possible at compile time?
With Kotlin, I have been avoiding builder pattern, as we can always specify default values for non-mandatory fields.
If you still want to use a builder pattern, you can use Step builder pattern that expects all mandatory fields to be set before creating the object. Note that each setter method returns the reference of next setter interface. You can have multiple Step builders based on the combination of mandatory fields.
class Model(val a: String = "", val b: String = "", val c: String = "")
class StepBuilder {
companion object {
fun builder(): AStep = Steps()
}
interface AStep {
fun setA(a: String): BStep
}
interface BStep {
fun setB(b: String): CStep
}
interface CStep {
fun setC(c: String): BuildStep
}
interface BuildStep {
//fun setOptionalField(x: String): BuildStep
fun build(): Model
}
class Steps : AStep, BStep, CStep, BuildStep {
private lateinit var a: String
private lateinit var b: String
private lateinit var c: String
override fun setA(a: String): BStep {
this.a = a
return this
}
override fun setB(b: String): CStep {
this.b = b
return this
}
override fun setC(c: String): BuildStep {
this.c = c
return this
}
override fun build() = Model(a, b , c)
}
}
fun main() {
// cannot build until you call all three setters
val model = StepBuilder.builder().setA("A").setB("B").setC("C").build()
}
I am really new to Kotlin and mostly coded in Javascript but I'm looking to see if I can dynamically create types while maintaining the static analysis.
This is what I'm looking to achieve.
class TestOne {
def one(){
print('function one');
}
}
class TestTwo {
def two() {
print('function two');
}
}
workItOut("TestOne").one() // prints "function one"
workItOut("TestTwo").two() // prints "function two"
The workItOut function will take the string and create the class
fun workItOut(name: String) {
if(name.equals("TestOne"))
return TestOne()
else if(name.equals("TestTwo"))
return TestTwo()
return ""
}
The typesafe way to achieve what you want is:
interface DoSomething {
fun foo()
}
class TestOne : DoSomething {
override fun foo() {
println("function one")
}
}
class TestTwo : DoSomething {
override fun foo() {
println("function two")
}
}
fun workItOut(name: String): DoSomething {
return when (name) {
"TestOne" -> TestOne()
"TestTwo" -> TestTwo()
else -> throw IllegalStateException("Invalid type identifier $name")
}
}
workItOut("TestOne").foo()
workItOut("TestTwo").foo()
The non-typesafe (and evil, non-kotlin, non-static typed) way is to use an unsafe cast and tell the function what result you expect to have (you seem to know what to expect because you are calling one() versus two()):
class TestOne {
fun one() {
println("function one")
}
}
class TestTwo {
fun twpo {
println("function two")
}
}
#Suppress("UNCHECKED_CAST")
fun <T: Any> workItOut(name: String): T {
return when (name) {
"TestOne" -> TestOne() as T
"TestTwo" -> TestTwo() as T
else -> throw IllegalStateException("Invalid type identifier $name")
}
}
workItOut<TestOne>("TestOne").one()
workItOut<TestTwo>("TestTwo").two()