使用 spock 框架测试状态机
Test a state machine using spock framework
我在 Java 中构建了一个状态机,我正在尝试使用 spock 框架对输入可以进行转换的所有可能状态组合进行单元测试。我更喜欢捕获数据 table 中的可能组合,但我面临着迭代值列表作为数据 table 中输入的挑战。请参考以下测试:
@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() 被设计为采用单一状态而不是arraylist 并验证结果。
有没有人做过类似的事情。我对 spock 有点陌生,但想了解是否可以测试第三种方法,否则请建议一种更好的方法来测试这个状态机
就像你说的:
This approach won't work since canTransitionTo() is not expecting an arraylist
因此,您需要确保遍历列表。多亏了 Groovy 的好意,你有了方便的方法。 next.every { current.canTransitionTo(it) }
怎么样?
package de.scrum_master.Whosebug.q68674245
import spock.lang.Specification
import spock.lang.Unroll
import static de.scrum_master.Whosebug.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.Whosebug.q68674245
import spock.lang.Specification
import spock.lang.Unroll
import static de.scrum_master.Whosebug.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
块。但我假设你的状态机比我的例子复杂一点,你需要指定更多不同的情况。
您还可以使用 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 中在线试用。
我在 Java 中构建了一个状态机,我正在尝试使用 spock 框架对输入可以进行转换的所有可能状态组合进行单元测试。我更喜欢捕获数据 table 中的可能组合,但我面临着迭代值列表作为数据 table 中输入的挑战。请参考以下测试:
@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() 被设计为采用单一状态而不是arraylist 并验证结果。
有没有人做过类似的事情。我对 spock 有点陌生,但想了解是否可以测试第三种方法,否则请建议一种更好的方法来测试这个状态机
就像你说的:
This approach won't work since canTransitionTo() is not expecting an arraylist
因此,您需要确保遍历列表。多亏了 Groovy 的好意,你有了方便的方法。 next.every { current.canTransitionTo(it) }
怎么样?
package de.scrum_master.Whosebug.q68674245
import spock.lang.Specification
import spock.lang.Unroll
import static de.scrum_master.Whosebug.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.Whosebug.q68674245
import spock.lang.Specification
import spock.lang.Unroll
import static de.scrum_master.Whosebug.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
块。但我假设你的状态机比我的例子复杂一点,你需要指定更多不同的情况。
您还可以使用 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 中在线试用。