You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spock测试中修改方法内部变量及验证else分支方法执行的实现方式

在Spock测试中修改方法内部变量并验证else分支执行

针对你的需求,我分两部分来解答:修改方法内部的局部变量,以及确保else分支里的方法被执行并验证它们的调用。

一、修改方法内部的局部变量(uName或uCat)

方法内部的局部变量(比如uNameuCat)默认无法直接从测试代码修改,因为它们的作用域仅限于userComment()方法内部。不过我们可以通过重构代码或者结合Groovy元编程/字节码工具来实现需求,推荐优先用重构的方式(更易维护):

方案1:重构代码,让变量可配置/可覆盖

把局部变量的来源改成可注入或可覆盖的方法,这样测试时可以轻松替换值:

class UserService { 
  // 把uName的获取改成可覆盖的方法
  protected String getUName() {
    return "test123"
  }

  void userComment() { 
    def uName = getUName() 
    def uCat = Category.getUserCategory(uName) 
    if (uCat.empty){
      println("Category is empty")
    } else {
      postComment();
      sendEmails();
    }
  } 

  void postComment() { /* 业务实现 */ }
  void sendEmails() { /* 业务实现 */ }
}

然后在Spock测试中,用Spy来覆盖getUName()方法,返回你想要的值:

def "修改uName为John并触发else分支"() {
  given:
  def userService = Spy(UserService) {
    // 覆盖getUName方法,返回"John"
    getUName() >> "John"
  }
  // 全局mock Category的静态方法,返回非空集合触发else分支
  GroovyMock(Category, global: true)
  Category.getUserCategory("John") >> ["some-category"]

  when:
  userService.userComment()

  then:
  // 验证else分支的方法被调用
  1 * userService.postComment()
  1 * userService.sendEmails()
}

如果要直接修改uCat的值,只需mockCategory.getUserCategory()的返回值即可,比如在另一个测试中让它返回包含"seller"的集合:

def "修改uCat为seller并测试"() {
  given:
  def userService = Spy(UserService)
  GroovyMock(Category, global: true)
  // 让getUserCategory返回包含"seller"的非空集合
  Category.getUserCategory(_) >> ["seller"]

  when:
  userService.userComment()

  then:
  1 * userService.postComment()
  1 * userService.sendEmails()
}

方案2:用PowerMock直接修改局部变量(不推荐,除非无法重构)

如果实在不能修改原代码,可以用PowerMock结合Spock来修改方法内部的局部变量。不过这种方式依赖字节码操作,可读性和维护性较差:

@RunWith(PowerMockRunner)
@PrepareForTest(UserService)
class UserServiceSpec extends Specification {
  def "直接修改uName局部变量"() {
    given:
    def userService = new UserService()
    GroovyMock(Category, global: true)
    Category.getUserCategory(_) >> ["seller"]

    when:
    // 使用PowerMock的Whitebox修改局部变量uName为"John"
    def method = UserService.getDeclaredMethod("userComment")
    Whitebox.invokeMethod(userService, method, new LocalVariable("uName", String.class, 0), "John")

    then:
    1 * userService.postComment()
    1 * userService.sendEmails()
  }
}

注意:PowerMock的使用会增加测试复杂度,尽量优先选择重构方案。

二、确保else分支的postComment和sendEmails被执行并验证

要让else分支执行,核心是让uCat.emptyfalse,也就是让Category.getUserCategory(uName)返回一个非空的集合。然后用Spock的SpyMock来验证这两个方法是否被调用:

完整的测试示例

import spock.lang.Specification
import spock.lang.Spy
import groovy.mock.interceptor.GroovyMock

class UserServiceSpec extends Specification {
  def "验证else分支的postComment和sendEmails被执行"() {
    given:
    // 创建UserService的Spy,跟踪真实对象的方法调用
    @Spy
    UserService userService = new UserService()
    // 全局mock Category的静态方法,返回非空集合触发else分支
    GroovyMock(Category, global: true)
    Category.getUserCategory(_) >> ["valid-category"]

    when:
    userService.userComment()

    then:
    // 验证postComment和sendEmails各被调用一次
    1 * userService.postComment()
    1 * userService.sendEmails()
    // 可选:确保if分支的println没有被执行
    0 * println(_)
  }
}

关键点说明

  • 使用GroovyMock全局mockCategory类的静态方法,让它返回非空值,从而触发else分支。
  • 使用Spy包装UserService实例,既可以保留原方法的逻辑,又能跟踪方法调用情况。
  • then块中用1 * methodName()来验证方法被调用的次数,确保else分支的逻辑被执行。

内容的提问来源于stack exchange,提问作者CChang

火山引擎 最新活动