使用Reader时For推导在Scala单元测试中未完全执行步骤
解决Tagless Final测试中仅第一步执行的问题
我帮你排查下这个问题,核心原因有两个,咱们一步步来理清楚:
问题根源分析
副作用位置错误,违背Reader惰性特性
你当前的TestConsole里,println("ok1")这类副作用是直接写在方法体里的,而不是封装在Reader的执行逻辑中。这导致在构建program对象时,prompt方法被调用就直接输出了ok1,但后续的read和display方法只有在Reader被实际运行(传入环境参数)时才会被调用——但你并没有执行这关键的一步。未实际触发Reader的执行流程
program本质是一个Reader[TestEnv, Unit],调用.run只会得到一个TestEnv => Unit的函数,你必须传入具体的TestEnv实例,才能触发整个for-comprehension流程的完整执行。
修复步骤
1. 修正TestConsole的副作用封装
把所有副作用(比如println)移到Reader的运行逻辑里,确保只有当Reader被执行时才触发:
implicit object TestConsole extends Console[Test] { override def prompt: Test[Unit] = Reader(TestEnv => { println("ok1") TestEnv.prompt }) override def read: Test[String] = Reader(TestEnv => { println("ok2") TestEnv.read }) override def display(fullName: FullName): Test[Unit] = Reader(TestEnv => { println("ok3") TestEnv.display(fullName.toString) }) }
2. 传入TestEnv并执行整个流程
先定义你的TestEnv实例,再传入.run方法触发完整流程:
// 假设你的TestEnv定义如下 case class TestEnv( prompt: Unit, read: String, display: String => Unit ) // 创建测试用的环境实例 val testEnv = TestEnv( prompt = (), read = "Alice Smith", // 模拟用户输入的名字 display = (msg: String) => println(s"最终显示内容: $msg") ) // 执行整个program流程 val result = new NameThing[Test]().program.run(testEnv)
额外提示:修复parse方法的潜在bug
你的parse方法在输入名字没有空格时会抛出ArrayIndexOutOfBoundsException,建议添加容错处理:
def parse(rawName: String): FullName = { val parts = rawName.split(" ", 2) // 最多拆分为两部分,避免过度拆分 FullName( firstName = parts.headOption.getOrElse(""), lastName = parts.lift(1).getOrElse("") ) }
这样修改后,运行测试就能看到ok1、ok2、ok3依次输出,整个流程都会被完整执行了。
内容的提问来源于stack exchange,提问作者jakstack
相关产品推荐
相关产品推荐

