在Kotlin中测试协同路由
我对一个爬虫做了一个简单的测试,它应该调用回购协议40次:在Kotlin中测试协同路由,kotlin,junit,kotlin-coroutines,Kotlin,Junit,Kotlin Coroutines,我对一个爬虫做了一个简单的测试,它应该调用回购协议40次: @Test fun testX() { // ... runBlocking { crawlYelp.concurrentCrawl() // Thread.sleep(5000) // works if I un-comment } verify(restaurantsRepository, times(40)).saveAll(restaurants) // ... }
@Test
fun testX() {
// ...
runBlocking {
crawlYelp.concurrentCrawl()
// Thread.sleep(5000) // works if I un-comment
}
verify(restaurantsRepository, times(40)).saveAll(restaurants)
// ...
}
这一实施:
suspend fun concurrentCrawl() {
cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.async {
val rests = scrapYelp.scrap(loc, start * 10)
restaurantsRepository.saveAll(rests)
}
}
}
}
但是。。。我明白了:
Wanted 40 times:
-> at ....testConcurrentCrawl(CrawlYelpTest.kt:46)
But was 30 times:
(30一直在变化;因此似乎测试没有等待…)
为什么我睡觉的时候它就过去了?如果我运行阻塞,则不需要它
顺便说一句,我有一个应该保持异步的控制器:
@PostMapping("crawl")
suspend fun crawl(): String {
crawlYelp.concurrentCrawl()
return "crawling" // this is supposed to be returned right away
}
谢谢
runBlocking
等待所有挂起函数完成,但由于concurrentclawl
基本上只是使用GlobalScope.async
currentclawl
在新线程中启动新作业,因此runBlocking
,在所有作业启动后完成,而不是在所有作业完成后完成
您必须等待以GlobalScope.async
启动的所有作业以如下方式完成:
suspend fun concurrentCrawl() {
cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.async {
val rests = scrapYelp.scrap(loc, start * 10)
restaurantsRepository.saveAll(rests)
}
}.awaitAll()
}
}
如果要等待concurrentCrawl()
在concurrentCrawl()
之外完成,则必须将延迟的
结果传递给调用函数,如以下示例所示。在这种情况下,可以从concurrentCrawl()
中删除suspend
关键字
你也可以用这个(还有更多)
MockK的verify
有一个timeout:Long
参数,专门用于在测试中处理这些比赛
您可以保持生产代码不变,并将测试更改为:
import io.mockk.verify
@Test
fun `test X`() = runBlocking {
// ...
crawlYelp.concurrentCrawl()
verify(exactly = 40, timeout = 5000L) {
restaurantsRepository.saveAll(restaurants)
}
// ...
}
如果在5秒之前的任何时间点验证成功,它将通过并继续。否则,验证(和测试)将失败。谢谢!但这难道不会导致
concurrentCall
被阻塞吗?或者是线程上下文中的awaittall
?是的,这会导致concurrentCrawl
阻塞,但单个爬网将并行执行。如果要等待作业在concurrentCrawl
之外继续,则必须将作业传递给调用函数。在我的回答中,我举了一个例子,就是需要挂起?从文件上看,我不确定什么时候应该使用它。谢谢在第一种情况下,awaitAll
挂起,因此concurrentCrawl
必须是一个suspend fun
。在第二种情况下,它从不挂起,因此它不应该是一个suspend fun
。另外,当您没有从协同程序中获得结果值时,不要使用async
。基本上,Deferred
是一种代码本身的味道。。暂停意味着它可以并行运行或暂停运行而不阻塞主流,对吗?关于延迟
,我想你的意思是我应该使用启动
。
fun concurrentCrawl(): List<Job> {
return cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.launch {
println("hallo world $start")
}
}
}.flatten()
}
runBlocking {
concurrentCrawl().joinAll()
}
import io.mockk.verify
@Test
fun `test X`() = runBlocking {
// ...
crawlYelp.concurrentCrawl()
verify(exactly = 40, timeout = 5000L) {
restaurantsRepository.saveAll(restaurants)
}
// ...
}