Scala中如何Mock LocalDate.now()?测试func1的something方法
如何Mock Scala Object中的something2方法
因为Scala的object是单例实现,直接mock它的方法不像普通类那么容易,下面给你两种可行的方案:
方案一:重构代码(推荐)
最稳妥的方式是先重构代码,让依赖可注入,避免直接依赖单例的something2方法,测试起来更灵活:
方式1:将逻辑提取到特质中
把something2的逻辑抽到特质里,让func1依赖这个特质,测试时替换成mock实现:
trait DateProvider { def getDate(): LocalDate } object func1 extends DateProvider { def something(arg1: Int): String = { val date = getDate() // 原业务逻辑 s"Processed $arg1 with date $date" } override def getDate(): LocalDate = LocalDate.now() }
测试时,直接实现一个测试用的DateProvider:
class Func1Test extends AnyWordSpec { "something method" should { "use mocked date" in { val mockDate = LocalDate.of(2024, 1, 1) val testFunc1 = new DateProvider { override def getDate(): LocalDate = mockDate } with { def something(arg1: Int): String = { val date = getDate() s"Processed $arg1 with date $date" } } val result = testFunc1.something(123) assert(result == "Processed 123 with date 2024-01-01") } } }
方式2:将something2作为参数传入
修改something方法,把something2作为可选参数,测试时传入mock的函数:
object func1 { def something(arg1: Int, dateFunc: () => LocalDate = something2): String = { val date = dateFunc() // 原业务逻辑 s"Processed $arg1 with date $date" } def something2(): LocalDate = LocalDate.now() }
测试时直接传mock的函数:
class Func1Test extends AnyWordSpec { "something method" should { "use mocked date" in { val mockDate = LocalDate.of(2024, 1, 1) val result = func1.something(123, () => mockDate) assert(result.contains("2024-01-01")) } } }
方案二:使用PowerMock直接Mock单例(不推荐,仅用于无法重构的场景)
如果实在不能重构代码,可以用PowerMock配合Mockito来mock Scala单例的方法。需要先添加测试依赖,再编写测试:
1. 添加依赖(以sbt为例)
libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "3.2.15" % Test, "org.powermock" % "powermock-module-junit4" % "2.0.9" % Test, "org.powermock" % "powermock-api-mockito2" % "2.0.9" % Test, "org.mockito" %% "mockito-scala" % "1.17.12" % Test )
2. 编写测试代码
import org.junit.runner.RunWith import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.modules.junit4.PowerMockRunner import org.powermock.api.mockito.PowerMockito import org.scalatest.BeforeAndAfterEach import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @RunWith(classOf[PowerMockRunner]) @PrepareForTest(Array(classOf[func1])) class Func1PowerMockTest extends AnyWordSpec with Matchers with BeforeAndAfterEach { override def beforeEach(): Unit = { PowerMockito.mockStatic(classOf[func1]) // 保留something方法的原实现 PowerMockito.when(func1.something(anyInt())).thenCallRealMethod() } "something method" should { "use mocked something2 result" in { val mockDate = LocalDate.of(2024, 1, 1) PowerMockito.when(func1.something2()).thenReturn(mockDate) val result = func1.something(123) result should include(mockDate.toString) } } }
注意:PowerMock会修改类加载器,可能带来兼容性问题,所以优先推荐重构代码的方案。
内容的提问来源于stack exchange,提问作者Pawan Asipu
相关产品推荐
相关产品推荐

