【问题标题】:Test a state machine using spock framework使用 spock 框架测试状态机
【发布时间】:2021-08-05 22:40:15
【问题描述】:

我已经用 Java 构建了一个状态机,我正在尝试使用 spock 框架对它进行单元测试,其中包含输入可以进行转换的所有可能的状态组合。我更喜欢在数据表中捕获可能的组合,但在将值列表迭代为数据表中的输入时我面临挑战。请参考以下测试:

@Unroll("#current state can successfully transit to #next")
def "Validate the successful transition of the state machine"() {
    given:
    when:
    def result = current.canTransitionTo(next)
    then:
    result == true

    where:
    current | next
    STATE_A | STATE_B // Works 
    STATE_A | STATE_C // Works
    STATE_A | [STATE_B, STATE_C] // This approach won't work since canTransitionTo() is not expecting an arraylist
}

前两种方法有效,但是我正在寻找一种方法使第三种方法在 spock 框架需要迭代所有可能的下一个值的情况下工作,因为 canTransitionTo() 被设计为采用单个状态而不是数组列表和验证结果。

有没有人做过类似的事情。我对 spock 有点陌生,但想了解是否可以测试第三种方法,否则请提出更好的方法来测试此状态机

【问题讨论】:

    标签: java groovy junit spock


    【解决方案1】:

    就像你说的:

    这种方法不起作用,因为 canTransitionTo() 不需要数组列表

    因此,您需要确保遍历列表。多亏了 Groovy 的优点,您有了方便的方法。 next.every { current.canTransitionTo(it) }呢?

    package de.scrum_master.stackoverflow.q68674245
    
    import spock.lang.Specification
    import spock.lang.Unroll
    
    import static de.scrum_master.stackoverflow.q68674245.StateMachineTest.StateMachine.*
    
    class StateMachineTest extends Specification {
      @Unroll("#current state can successfully transit to #next")
      def "Validate the successful transition of the state machine"() {
        expect:
        next.every { current.canTransitionTo(it) }
    
        where:
        current | next
        STATE_A | STATE_B
        STATE_A | STATE_C
        STATE_A | [STATE_B, STATE_C]
        START   | [STATE_A, STATE_B, STATE_C]
        STATE_C | [STATE_B, START]  // fails, because START is a forbidden target state
      }
    
      static enum StateMachine {
        START, STATE_A, STATE_B, STATE_C
    
        boolean canTransitionTo(StateMachine stateMachine) {
          println "transition $this -> $stateMachine"
          return stateMachine != START
        }
      }
    }
    

    您甚至可以更进一步,在转换的两端允许迭代:

    package de.scrum_master.stackoverflow.q68674245
    
    import spock.lang.Specification
    import spock.lang.Unroll
    
    import static de.scrum_master.stackoverflow.q68674245.StateMachineTest.StateMachine.*
    
    class StateMachineTest extends Specification {
      @Unroll("#current can transit to #next")
      def "Validate successful transition of the state machine"() {
        expect:
        current.every { from -> next.every { to -> from.canTransitionTo(to) } }
    
        where:
        current                            | next
        [START, STATE_A, STATE_B, STATE_C] | [STATE_A, STATE_B, STATE_C]
      }
    
      @Unroll("#current must not transit to #next")
      def "Validate failed state machine transition"() {
        expect:
        current.every { from -> next.every { to -> !from.canTransitionTo(to) } }
    
        where:
        current                            | next
        [START, STATE_A, STATE_B, STATE_C] | START
      }
    
      static enum StateMachine {
        START, STATE_A, STATE_B, STATE_C
    
        boolean canTransitionTo(StateMachine stateMachine) {
          println "transition $this -> $stateMachine"
          return stateMachine != START
        }
      }
    }
    

    P.S.:在上述情况下,如果数据表仅包含单行,则实际上不需要 where 块。但我假设您的状态机比我的示例复杂一些,您需要指定更多差异化的情况。

    【讨论】:

    • 感谢您的帮助。但是只是想对我的实际用例进行增强,如何添加一个额外的列来表示 actionedBy ,它表示一个系统,并有条件地调用没有 if 子句的方法。例如:系统 1 |STATE_A | STATE_B 调用 canTransitToSystem1State() 和 System2 |STATE_A | STATE_B 调用 canTransitToSystemsState()。我尝试了多个时间,然后是代码,但它没有用
    • 我不明白你的后续问题。我想我已经正确回答了最初的问题,并且解释的比你问的还要多。所以,请接受并支持我的回答并提出一个新问题。但是这一次,发布一个完整的MCVE,就像我在回答中所做的那样。我需要一些我可以编译和运行的东西。我需要完整的类:被测类、依赖类(如果有的话)、测试。那我就更明白你想要什么了。
    【解决方案2】:

    您也可以使用combinations() 来生成您想要的不同组合。

    class ASpec extends Specification {
        def "can transition"() {
            expect: true
            where:
            [from, to] << 
                [["STATE_A"], ["STATE_B", "STATE_C"]].combinations() + 
                [["STATE_B", "STATE_C"], ["STATE_C", "STATE_D"]].combinations()
        }
    }
    

    这会产生

    ╷
    └─ Spock ✔
       └─ ASpec ✔
          └─ can transition ✔
             ├─ can transition [from: STATE_A, to: STATE_B, #0] ✔
             ├─ can transition [from: STATE_A, to: STATE_C, #1] ✔
             ├─ can transition [from: STATE_B, to: STATE_C, #2] ✔
             ├─ can transition [from: STATE_C, to: STATE_C, #3] ✔
             ├─ can transition [from: STATE_B, to: STATE_D, #4] ✔
             └─ can transition [from: STATE_C, to: STATE_D, #5] ✔
    

    您可以在groovy webconsole在线试用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-27
      • 1970-01-01
      • 2021-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多