测试是开发人员生活中必不可少的一部分。经过测试的代码更易于维护,并且测试通常也用作文档。
如果您一直在使用 Kotlin,那么您之前可能已经编写过大量测试,但是您是否尝试过使用Kotlin Playground这样做?
在本文中,我们将演示如何使用 Kotlin Playground 来测试您的代码。如何修复使命召唤战区2错误代码Diver?我们还将分享一些可用于简化异步代码的高级协程概念。
跳跃前进:
什么是 Kotlin 游乐场?
游乐场入门
了解高级 Kotlin 协程
测试高级协程测试正常挂起功能切换/注入测试调度程序测试启动为什么PUBG在启动时不断崩溃?pubg一启动就崩溃怎么修复/异步测试工作/主管工作测试流程
改进测试的策略命名你的 CoroutineScope遵循协程最佳实践
有时我们只是想要一种快速的方法来测试某些东西,如何修复Dropbox的错误404?但是打开 Android Studio 或其他代码编辑器需要一些时间。如果有一种方法可以快速测试您的想法怎么办?嗯,这就是 Kotlin Playground 的意义所在。
Playground 是一个能够运行 Kotlin 代码的编辑器,如何修复MicrosoftTeams错误代码4c7最重要的是,它可以在您的浏览器上运行。它由 JetBrains 开发和维护。steam商店点击没反应怎么办?解决Steam商店按钮不起作用问题的方法要访问它,请前往play.kotlinlang.org。
Kotlin Playground 附带所有标准 Kotlin 库(集合、反射、协程等),但不支持添加新库,这意味着您只能使用它来制作原型或测试依赖于标准 Kotlin 库的东西。
Kotlin Playground 的一大特色是它使您能够轻松地与其他人共享您编写的代码。如何修复Roblox错误代码529?roblox错误代码279怎么回事只需复制页面 URL 并将其发送给其他人或使用“共享代码”按钮即可获得代码的可嵌入版本。
Kotlin Playground 中还有一个隐藏功能。如果您在四处单击时按住 Ctrl (Windows) 或 Command (Mac) 键,则会创建更多光标。如何修复Zoom帐户已禁用的错误?zoom账户被锁解决方法如果您需要同时编辑多行代码,这将非常有用:
自 2022 年 5 月起,Playground 也支持移动设备,因此您甚至不需要计算机即可运行 Kotlin 代码。还添加了一个操作工具栏,允许您选择 Kotlin 版本、xbox很抱歉我们无法显示game pass是什么原因?怎么修复无法显示game pass选择编译器和设置程序参数。
为了在 Playground 中运行代码如何修复Windows10鼠标双击?win10鼠标变成双击了怎么调回来,我们需要创建一个 main 方法;否则,我们会得到一个“No main method found in project”的错误。
在本教程中,我将使用 Kotlin v1.7.21 和 JVM 编译器。如何修复RPC服务器在Windows10上不可用?rpc服务器不可用 win10解决方法请务必将以下导入添加到文件顶部:
import kotlin.test.*
import kotlinx.coroutines.*
这样做之后你应该有一个看起来像这样的游乐场:
如果您按下紫色的运行按钮,代码应该可以编译,您应该看不到任何错误。
在我们深入测试之前,让我们回顾一下Kotlin 协程的一些概念。
CoroutineContext是协程的重要组成部分,window11怎么返回window10?win11回退到win10教程它定义了协程的行为方式,并具有四个共同元素:
Job:控制协程的生命周期;此元素默认为 a,但您也可以指定Job SupervisorJob
CoroutineDispatcher:定义协程应该在哪个线程组中执行;大多数时候你会使用Main或IO调度员
CoroutineExceptionHandler: 定义未捕获的异常应该发生什么
CoroutineName: 为协程定义一个名称(本文后面会详细介绍)
如果您使用过withContextscope 函数,如何修复Valorant错误代码62?您可能想知道它的性能。如果您有很长的操作链,并且其中许多操作用于withContext确保工作在正确的线程中执行,这不会增加很大的性能开销吗?
如果我们看一下它的实现,我们会发现有两条快速路径。第一个将当前上下文与新上下文进行比较。如果它们相同,则无需更改线程。如何修复Windows错误0x80070015Windows?10种方法轻松解决第二条路径比较调度程序。如果它们相同,则也无需更改线程。
因此,如果调度程序相同,修复 Windows 更新失败并出现错误 0x80242016方法我们就不必担心线程,开销很小。
流是价值流。如果您需要合并它们,可以使用一个简洁的操作符,该combine操作符允许您根据需要合并任意数量的流。这是它最简单的签名:
fun
假设您有flowA并且flowB想要合并它们。电脑蓝光过滤软件有那些?护眼模式中的蓝光过滤怎么选为此,请使用以下代码:
flowA.combine(flowB) { a, b ->
// combine them and emit a new value
}
或者,您可以使用上述代码的顶级版本:
combine(flowA, flowB) { a, b ->
// combine them and emit a new value
}
现在,让我们继续进行测试。我们将了解如何测试正常suspend功能、如何切换/注入测试调度程序以及如何测试launch/ async、Job/SupervisorJob和流。
让我们从定义一个suspend函数开始,这样我们就有了一些东西来编写我们的测试。假设我们有一个函数返回是否启用了某个功能:
suspend fun isEnabled(): Boolean {
delay(1_000)
return true
}
现在,让我们在 main 函数中编写我们的测试。assertTrue由提供并断言我们传入的变量是;否则,它会抛出异常:kotlin.testtrue
fun main() {
runBlocking {
val result = isEnabled()
assertTrue(result)
}
}
如果我们运行这段代码,我们将不会得到任何输出,因为测试通过了,但如果我们更改isEnabled为返回false,我们将得到以下错误:
Exception in thread "main" java.lang.AssertionError: Expected value to be true.
如果您想向 中添加自定义消息assertTrue,请使用以下命令:
assertTrue(result, "result should be true but wasn't")
这将导致以下输出:
Exception in thread "DefaultDispatcher-worker-1 @coroutine#1" java.lang.AssertionError: result should be true but wasn't
在您的代码中硬编码调度程序不是好的做法。只要有可能,您应该接受调度程序作为您的类中的参数。
看看下面的代码:
class Database {
private val scope = CoroutineScope(Dispatchers.IO)
fun saveToDisk() {
scope.launch {
...
}
}
}
很难测试这个。为了使事情更简单,我们可以将其更改如下:
class Database(private val scope: CoroutineScope) {
fun saveToDisk() {
scope.launch {
...
}
}
}
这样,可以在测试期间注入作用域。
launch并且async可能是 中最常用的功能之一Compose,尤其是对于 Android 开发人员而言。那么,我们如何测试它们呢?
让我们从定义一个保存一些状态的简单函数开始。您不能调用suspend函数,因为您的 Activity 或 Fragment 中没有作用域。但是,使用协程可以帮助您避免阻塞主线程,这可能会在您将内容保存到后端时发生:
private val scope = CoroutineScope(Dispatchers.IO)
fun saveState() {
scope.launch {
// save state to backend, disk, ...
}
}
鉴于我们没有数据库或服务器,让我们创建一个变量来假装我们做了一些事情:
private val state: Any? = null
fun saveState() {
scope.launch {
state = "application state"
}
}
现在我们有了可测试的东西,让我们来编写我们的测试:
fun main() {
runBlocking {
saveState()
assertNotNull(state)
}
}
这应该有效,对吧?好吧,如果我们运行这段代码,我们会得到一个错误提示stateis null。这段代码的问题是它在调用saveState但没有等待它执行,所以我们在操作完成之前检查结果。
要解决这个问题,我们可以在检查之前简单地添加一个小延迟state,如下所示:
fun main() {
runBlocking {
saveState()
delay(100)
assertNotNull(state)
}
}
这样,saveState在我们检查变量之前就有时间执行。但是,使用它delay来测试您的代码并不是最佳做法。为什么是 100 毫秒而不是 200 毫秒?如果代码执行时间超过 100 毫秒怎么办?这就是为什么我们应该避免这种做法。在本文的稍后部分,我将向您展示一种更好的测试方法。
测试async是一个类似的过程;让我们修改saveState它使用async:
fun saveState() {
scope.launch {
async { state = "application state" }.await()
}
}
fun main() {
runBlocking {
saveState()
delay(100)
assertNotNull(state)
}
}
现在,运行这段代码;您会看到它按预期工作。
接下来,让我们探讨一下如何测试Jobs. 如果您使用它,GlobalScope则很难测试您的代码,因为您无法替换或模拟它。此外,由于您无法取消GlobalScope使用它,因此您基本上失去了对工作的控制。相反,我们将为我们的测试定义一个自定义范围,如果需要我们可以控制它:
private val scope = CoroutineScope(Dispatchers.Default)
我们可以定义一个变量来跟踪Job并修改saveState以将结果分配launch给该变量:
private var job: Job? = null
private var state: Any? = null
fun saveState() {
job = scope.launch {
println("application state")
}
}
现在,在主函数中我们可以测试saveState函数:
fun main() {
runBlocking {
saveState()
job?.join()
assertNotNull(state)
}
}
运行这个,你不应该得到任何错误。
你可能会问,“为什么我们需要使用join?” 嗯,调用launch不会阻塞代码执行,所以我们需要使用join来防止main函数退出。
现在我们知道如何测试 a Job,让我们学习如何测试 a SupervisorJob。
ASupervisorJob类似于 a Job,除了它的子节点可以彼此独立地失败。让我们首先更改我们的范围以拥有一个 SupervisorJob:
val scope = CoroutineScope(SupervisorJob())
现在在我们的 main 函数中,让我们添加另一个只launch抛出错误的函数:
fun main() {
runBlocking {
scope.launch { throw error("launch1") }.join()
saveState().join()
assertNotNull(state)
}
}
运行它,你会在输出中看到一个错误。那么,不应该SupervisorJob阻止它吗?如果我们更详细地分析它,我们会发现它实际上确实阻止了错误的冒泡,但它并没有阻止它被记录下来。如果你println在断言下面添加一个语句,你会看到它实际上被打印出来了;因此,即使第一个launch抛出错误,第二个也能运行。
为了测试流程,我们将从添加一个新的导入开始:
import kotlinx.coroutines.flow.*
observeData接下来,让我们创建一个仅返回流的函数:
fun observeData(): Flow
return flowOf("a", "b", "c")
}
现在,在我们的 main 方法中,我们可以使用 assertEquals 函数来比较期望值和实际值:
suspend fun main() = runBlocking
val expected = listOf("a", "b", "c")
assertEquals(expected, observeData().toList())
}
现在我们对如何测试高级协程有了更好的了解,让我们看看一些使协程测试和调试更容易的策略。
如果你有很多协程范围,调试它们可能会很困难,因为它们都使用类似于@coroutine#1、@coroutine#2 等的命名约定。
为了使调试更容易,我们可以添加到,如下所示:CoroutineName(...)CoroutineScope
private val scope = CoroutineScope(Dispatchers.Default + CoroutineName("database"))
如果在该范围内出现问题,我们将收到如下错误:
Exception in thread "DefaultDispatcher-worker-1 @database#2" java.lang.IllegalStateException: …
为了使测试更容易,请遵循以下协程最佳实践:
将调度程序注入您的类:避免在您的类中对调度程序进行硬编码。注入它通过允许您替换它们来简化测试
避免GlobalScope:它使测试变得非常困难,这本身就是避免它的一个很好的理由。它还使控制作业的生命周期变得更加困难
在本文中,我们探讨了如何使用 Kotlin Playground 来测试协程。我们还研究了一些协程概念,例如CoroutineContext和Flow。最后,我们讨论了一些使测试更容易的策略。
现在轮到你了; 学习东西的最好方法是练习。下次见!