Keyword: Coroutine mock
直到27日,完成KMM的测试功能放在
KMMDay27
今天开始要来写KMM的测试,由於我们使用DB或是进行网路请求都是suspend function,所以需要先撰写一个给测试使用的基础Coroutine方便我们进行测试.
首先,在CommonTest的专案底下,建立一个expect物件,BaseTest,里面只有一个runTest的方法.
//这是在commonTest底下的Kotlin
import kotlinx.coroutines.CoroutineScope
expect abstract class BaseTest() {
fun <T> runTest(block: suspend CoroutineScope.() -> T)
}
有了expect当然就需要去双平台实作actual让双平台在测试时候有办法使用,跟commonMain的expect物件一样.必须有同样的packagePath
Android的实作方法如下,其中的coroutineTestRule是我们自己写的Rule,他会在测试开始与结束时重设Dispather,避免测试的顺序影响到测试的结果..
// 这是在androidTest 底下的Kotlin
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
actual abstract class BaseTest {
@get:Rule
var coroutineTestRule = CoroutineTestRule()
actual fun <T> runTest(block: suspend CoroutineScope.() -> T) {
runBlocking { block() }
}
}
CouroutineTestRule如下,非常简单的结构
// 这是在androidTest 底下的Kotlin
class CoroutineTestRule(
private val testDispatcher: ExecutorCoroutineDispatcher = Executors.newSingleThreadExecutor()
.asCoroutineDispatcher()
) : TestWatcher() {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)//设定Main
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()//重设Main
}
}
这边我们有用到junit等等的常用测试工具,所以要在gradle(shared)的androidTest部分加上他们的引用
//这是在build.gradle.kts中的Kotlin
...
val androidTest by getting {
dependencies {
...
implementation(Develop.KotlinTest.jvm)
implementation(Develop.KotlinTest.junit)
implementation(Develop.AndroidXTest.core)
implementation(Develop.AndroidXTest.junit)
implementation(Develop.AndroidXTest.runner)
implementation(Develop.AndroidXTest.rules)
...
}
}
...
//这是在buildSrc内的Kotlin设定
Develop{
object AndroidXTest {
val core = "androidx.test:core:${Versions.AndroidX.test}"
val junit = "androidx.test.ext:junit:${Versions.AndroidX.test_ext}"
val runner = "androidx.test:runner:${Versions.AndroidX.test}"
val rules = "androidx.test:rules:${Versions.AndroidX.test}"
}
object KotlinTest {
val common = "org.jetbrains.kotlin:kotlin-test-common:${Versions.kotlin}"
val annotations = "org.jetbrains.kotlin:kotlin-test-annotations-common:${Versions.kotlin}"
val jvm = "org.jetbrains.kotlin:kotlin-test:${Versions.kotlin}"
val junit = "org.jetbrains.kotlin:kotlin-test-junit:${Versions.kotlin}"
}
}
Versions{
val kotlin = "1.5.21"
}
这样我们就准备好Android的测试Coroutine了,接下来再去实作iOS使用的测试Coroutine
同样的,在同样的package path下,建立与expect同名的actual实作
//这是在iosTest底下的Kotlin语言
actual abstract class BaseTest {
@OptIn(DelicateCoroutinesApi::class)
actual fun <T> runTest(block: suspend CoroutineScope.() -> T) {
var error: Throwable? = null
//由於iOS没有Coroutine环境,所以改成GlobalScope来进行测试
GlobalScope.launch(Dispatchers.Main) {
try {
block()
} catch (t: Throwable) {
error = t
} finally {
CFRunLoopStop(CFRunLoopGetCurrent())//这边是由Native提供的iOS方法
}
}
CFRunLoopRun()//这边是由Native提供的iOS方法
error?.also { throw it }
}
}
有了这两个基础的测试後,就有coroutine环境可以进行更进一步的测试
<<: 成为工具人应有的工具包-15 PasswordFox
>>: D15 - 彭彭的课程# Python 函式参数详解:参数预设值、名称对应、任意长度参数(2)
常听说出事了就想找老大摆平, 结果在群内的地位越来越低, 如果能试着自己处理问题, 把事情Handl...
接续此篇: https://ithelp.ithome.com.tw/articles/102649...
前言 直接将所有 Python 程序写在工作簿内的第一种 TabPy 使用方法我们已经学会了,但这种...
档案的读写算是Python非常实用的一环,可以帮助我们去编辑、储存或是新增建立一个档案。 在Pyrh...
如果用非常概括性的说法来描述 CPU 的组成,其实就是数以亿计的电晶体组合而成的。那麽电晶体又是怎麽...