【问题标题】:How to verify interactions within the class under test?如何验证被测类内的交互?
【发布时间】:2021-06-12 15:12:13
【问题描述】:
// class under specification
public class TeamService {

  // method under specification
  public void deleteTeam(String id) {
     /* some other calls */
     this.moveAssets(team) // calls method within the class under spec. 
  }

  // I would like to stub / mock this method
  public void moveAssets(Team team){
    // logic
  } 
  
}

Spock 规范

def deleteTeam(){
   given: 
   TeamService teamService = new TeamService()

   when: 'I delete the team'
   teamService.deleteTeam()
   
   then: 'I want to check that moveAssets gets called(team resources going to be preserved 
and that deleteTeam (external class that deletes the team) gets called (has no issues here)'

   1 * teamService.moveAssets(id) >> {} //See Q1
/* 
Q1: I want to test that this call is made 
but currently it does not count as interaction for some reason so I get an error. 
From what I read you can't stub the calls unless the class is mocked -
 
but here I do have an important need to check on the method within the class under specification 
(that is not mocked). What are my options?

I know I can in theory move moveAssets to some other class,
which I can then mock and accomplish it but that does not feel right. 
*/
}

所以在Spock对这个类的Spec中,TeamService是规范下的类,deleteTeam是规范下的方法。

问题 我收到 moveAssets 方法的错误 0 调用。 我希望能够存根/测试 moveAssets 方法,没有太多关于如何完成该方法的信息。 我不想将 moveAssets 重新定位到 anotherClass (所以我可以模拟它)。 任何指导表示赞赏。

必须有一些更好的方法来处理这种情况。


总结:

如何测试从“规范下的方法 (teamServices.deleteTeam())”调用的“规范下的类”teamServices.moveAssets()) 方法?


https://github.com/spockframework/spock/discussions/1346

【问题讨论】:

    标签: java unit-testing groovy mocking spock


    【解决方案1】:

    就像您已经注意到的那样,您只能检查模拟对象类型的交互,即模拟、存根或间谍。后者,spy,是您在这种情况下所需要的,例如:

    TeamService teamService =
      Spy(constructorArgs: [mockedInjectedService1, mockedInjectedService2])
    

    这是MCVE

    package de.scrum_master.stackoverflow.q67950174
    
    class Team {
      String id
    }
    
    package de.scrum_master.stackoverflow.q67950174
    
    class TeamService {
      def dep1, dep2
    
      TeamService(dep1, dep2) {
        this.dep1 = dep1
        this.dep2 = dep2
      }
    
      boolean moveAssets(Team team) {
        println "Moving assets for team $team"
        return false
      }
    
      void deleteTeam(String id) {
        Team team = findTeamById(id)
        moveAssets(team)
        delete(team)
      }
    
      Team findTeamById(String id) {
        println "Searching for team $team"
        return new Team(id: id)
      }
    
      void delete(Team team) {
        println "Deleting team $team"
      }
    }
    
    package de.scrum_master.stackoverflow.q67950174
    
    import spock.lang.Specification
    
    class TeamServiceTest extends Specification {
      def deleteTeam() {
        given:
        Team teamDocument = new Team(id: "dummy")
        String id = "foo"
        def mockedInjectedService1 = "X"
        def mockedInjectedService2 = "Y"
        TeamService teamService = Spy(constructorArgs: [mockedInjectedService1, mockedInjectedService2])
    
        when: 'I delete the team'
        teamService.deleteTeam(id)
    
        then: 'I want to check that moveAssets gets called(team resources going to be preserved'
        1 * teamService.findTeamById(id) >> teamDocument
        1 * teamService.moveAssets(teamDocument) >> true
      }
    }
    

    【讨论】:

    • 很好的例子,我只是很困惑为什么你添加了两个额外的模拟作为依赖项,而 OPs TeamService 没有任何构造函数参数。
    • 我最初问这些问题时有这些依赖关系,然后意识到它们与问题不太相关。在原始代码中,我确实将模拟作为依赖项。抱歉,各位,有什么困惑。非常感谢您的反馈和帮助。
    • @LeonardBrünings,因为我回答了这个问题的this version,试图表现得很好并尽可能多地使用原始代码,以便让 OP 更容易,不幸的是,他是不明智的(我在这里尽量避免使用另一个形容词)足以在问题已经回答后简单地修改问题,从而造成混乱。无论如何,至少我现在的回答显示了如何使用constructorArgs,这是 OP 真正需要的。
    • @kriegaex,让我们保持专业。我唯一的目的是让我的问题更清楚,因为我在最初的问题中没有做到这一点。老实说,我尽我所能做到最好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-15
    • 2013-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    相关资源
    最近更新 更多