Scala+Mockito:如何Mock方法内部调用的方法返回结果?
解决Scala+Mockito测试中
getHotels方法未被Mock的问题 你遇到的问题核心是Mock对象搞错了,还混用了测试框架,导致getHotels没有被正确Stub,所以实际调用了真实方法。咱们一步步捋清楚问题,再给出修正方案:
先说说你测试代码里的几个问题
- 混用了ScalaMock和Mockito:你同时继承了
MockFactory(ScalaMock特性)和用了Mockito的@Mock、@InjectMocks,这俩框架的Mock机制不一样,会互相干扰,选一个用就行,这里咱们就用Mockito。 - Mock错了对象:你要Mock的是
HotelsService依赖的HotelsServiceInt接口,而不是HotelsService本身!因为HotelsService的getHotels方法是直接转发给hotelsServiceInt的,你MockHotelsService的getHotels根本没用,实际代码调用的是成员变量hotelsServiceInt的方法。 - 返回值类型不匹配:
HotelsServiceInt的getHotels返回的是Observable<List<String>>,但你直接Stub返回了List,类型不对,Mockito没法正确识别这个Stub。 - 初始化逻辑混乱:你手动new了
hotelsServiceInjectedMock,又用MockitoAnnotations.initMocks(this),这俩方式冲突,要么用注解注入,要么手动构造实例。
修正后的测试代码
咱们重新写一个正确的测试类,用纯Mockito来实现:
import org.mockito._ import org.mockito.Mockito._ import org.scalatest.{BeforeAndAfter, FunSpec} import rx.Observable import java.util.ArrayList class HotelsServiceSpec extends FunSpec with BeforeAndAfter { // 要Mock的是依赖的接口HotelsServiceInt @Mock private var hotelsServiceIntMock: HotelsServiceInt = _ // 待测试的对象,手动传入Mock的依赖(或者用@InjectMocks也可以) private var hotelsService: HotelsService = _ before { // 初始化Mockito的Mock对象 MockitoAnnotations.initMocks(this) // 手动构造待测试对象,把Mock的接口传进去 hotelsService = new HotelsService(hotelsServiceIntMock) } describe("HotelsService doSomeBusinessLogic") { it("Should return false when hotels meet certain condition") { val val1 = 1L val val2 = 2L // 构造测试用的酒店列表,并包装成Observable(因为原方法返回Observable) val testHotels = new ArrayList[String]() testHotels.add("Hilton") testHotels.add("Sheraton") testHotels.add("Rotana") // Stub HotelsServiceInt的getHotels方法,返回我们构造的Observable doReturn(Observable.just(testHotels)) .when(hotelsServiceIntMock) .getHotels(Matchers.anyLong(), Matchers.anyLong()) // 执行待测试方法 val result = hotelsService.doSomeBusinessLogic(val1, val2) // 验证结果 assert(result == false) // 可选:验证Mock方法被调用了一次 verify(hotelsServiceIntMock, times(1)).getHotels(val1, val2) } } }
关键修正点解释
- 只使用Mockito:去掉了ScalaMock的
MockFactory,避免框架冲突。 - Mock正确的依赖:现在Mock的是
HotelsServiceInt接口,这才是HotelsService真正依赖的对象,Stub它的getHotels方法才能拦截实际调用。 - 匹配返回值类型:用
Observable.just(testHotels)把列表包装成Observable,和原方法的返回类型一致。 - 清晰的初始化逻辑:先初始化Mock对象,再手动构造待测试的
HotelsService,把Mock依赖传进去,逻辑更清晰,也避免了注解注入的潜在问题。
这样修改后,当你运行测试时,doSomeBusinessLogic里调用getHotels就会触发我们Stub的Mock返回,不会再调用真实的HotelsServiceInt实现了。
内容的提问来源于stack exchange,提问作者Ababneh A
相关产品推荐
相关产品推荐

