使用 Kotlin Playground 测试高级协程

测试是开发人员生活中必不可少的一部分。经过测试的代码更易于维护,并且测试通常也用作文档。

如果您一直在使用 Kotlin,那么您之前可能已经编写过大量测试,但是您是否尝试过使用Kotlin Playground这样做?

在本文中,我们将演示如何使用 Kotlin Playground 来测试您的代码。如何修复使命召唤战区2错误代码Diver?我们还将分享一些可用于简化异步代码的高级协程概念。

跳跃前进:

  • 什么是 Kotlin 游乐场?

  • 游乐场入门

  • 了解高级 Kotlin 协程

  • 改进测试的策略命名你的 CoroutineScope遵循协程最佳实践

什么是 Kotlin 游乐场?

有时我们只是想要一种快速的方法来测试某些东西,如何修复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 协程

在我们深入测试之前,让我们回顾一下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 <T1, T2, R> Flow<T1>.combine(flow: Flow<T2>, transform: suspend (T1, T2) -> R): Flow<R>

假设您有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函数开始,这样我们就有了一些东西来编写我们的测试。假设我们有一个函数返回是否启用了某个功能:

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

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)

}

}

现在,运行这段代码;您会看到它按预期工作。

测试Job/SupervisorJob

接下来,让我们探讨一下如何测试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<String> {

return flowOf("a", "b", "c")

}

现在,在我们的 main 方法中,我们可以使用 assertEquals 函数来比较期望值和实际值:

suspend fun main() = runBlocking<Unit> {

val expected = listOf("a", "b", "c")

assertEquals(expected, observeData().toList())

}

改进测试的策略

现在我们对如何测试高级协程有了更好的了解,让我们看看一些使协程测试和调试更容易的策略。

命名你的CoroutineScope

如果你有很多协程范围,调试它们可能会很困难,因为它们都使用类似于@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。最后,我们讨论了一些使测试更容易的策略。

现在轮到你了; 学习东西的最好方法是练习。下次见!

猜你喜欢

转载自blog.csdn.net/weixin_47967031/article/details/130092672