如何用MockFactory模拟含嵌套case class的Scala配置类?
解决ScalaMock模拟嵌套Case Class的空指针问题
刚好我之前也遇到过类似的场景,直接mock外层case class时,嵌套属性默认返回null,访问自然会触发空指针异常。下面给你两种简洁的解决方案,适配不同的测试需求:
1. 完全Mock嵌套结构(适合需要验证调用的场景)
如果你的测试需要验证嵌套配置的方法/属性是否被调用,就分别mock外层和内层的case class,然后关联它们的返回关系。
假设你的case class结构是这样的:
case class ProxyConfig(host: String, port: Int) case class SomeConfig(proxy: ProxyConfig, ftpPath: String, httpTimeout: Int)
测试代码示例:
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalamock.scalatest.MockFactory class ConfigTest extends AnyFlatSpec with Matchers with MockFactory { it should "mock nested case class without NPE" in { // 第一步:模拟内层的ProxyConfig val mockProxy = mock[ProxyConfig] // 设置ProxyConfig属性的返回值 (mockProxy.host _).expects().returning("test-proxy.example") (mockProxy.port _).expects().returning(8080) // 第二步:模拟外层的SomeConfig,并关联嵌套属性 val mockSomeConfig = mock[SomeConfig] // 让mock的SomeConfig返回刚才创建的mockProxy (mockSomeConfig.proxy _).expects().returning(mockProxy) // 同时设置其他属性的返回值 (mockSomeConfig.ftpPath _).expects().returning("/test/ftp/dir") (mockSomeConfig.httpTimeout _).expects().returning(5000) // 现在访问嵌套属性不会有空指针了 mockSomeConfig.proxy.host shouldBe "test-proxy.example" mockSomeConfig.proxy.port shouldBe 8080 } }
2. 使用Stub简化配置(适合仅需要假数据的场景)
如果你的测试不需要验证调用,只是需要一个能正常返回值的假配置,用stub代替mock会更简洁——stub不需要写expects(),直接用returns设置返回值即可:
it should "use stub for nested case class" in { // 创建内层ProxyConfig的stub val stubProxy = stub[ProxyConfig] stubProxy.host _ returns "stub-proxy.example" stubProxy.port _ returns 9090 // 创建外层SomeConfig的stub并关联 val stubSomeConfig = stub[SomeConfig] stubSomeConfig.proxy _ returns stubProxy stubSomeConfig.ftpPath _ returns "/stub/ftp/path" // 验证返回值 stubSomeConfig.proxy.host shouldBe "stub-proxy.example" }
为什么之前会有空指针?
当你直接mock[SomeConfig]时,ScalaMock对所有引用类型的属性默认返回null。当代码尝试访问someConfig.proxy.host时,proxy是null,自然就触发了空指针异常。解决的核心就是给外层mock的proxy属性设置一个有效的返回值——不管是另一个mock、stub,甚至是真实的ProxyConfig实例都可以。
比如如果不需要模拟ProxyConfig,直接用真实实例也能快速解决:
val realProxy = ProxyConfig("real-proxy", 8080) val mockSomeConfig = mock[SomeConfig] (mockSomeConfig.proxy _).expects().returning(realProxy)
内容的提问来源于stack exchange,提问作者juan garcia
相关产品推荐
相关产品推荐

