代码:
public class A{
public void method(User user){
String name = user.getName();
if("Tom".equals(name)){
method1(user);
}else{
method2(user);
}
}
}
我想使用spock编写一个关于A#method()的测试。正如你所看到的,当我测试method()时,我不关心method1()或method2()的执行,我只想验证这两个方法在某些情况下是否会被调用。 我的测试代码:
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
class ASpec extends Specification {
def "method"(){
given:
def process = new A()
expect:
process.method(input as User)
where:
input || _
new User(name:"Tom") || _
}
}
我发现method1()确实执行了,但我认为单元测试的意义只是验证过程是否正确,所以我不希望method1()实际执行,而只是调用。 我该怎么办?
附注 我也用间谍:
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
class ASpec extends Specification {
def "method"(){
given:
A process = Spy()
expect:
process.method(input as User)
where:
input || _
new User(name:"Tom") || _
}
}
但是method1()也执行了。
Leonard 是完全正确的:在 >99% 的正常单元测试中
你想要的在技术上是可能的,但如果有必要的话,它通常是一种代码味道。为了便于论证,让我们假设您的测试类如下所示:
class A {
void method(User user) {
if ("Tom" == user.name)
method1(user)
else
method2(user)
}
void method1(User user) {
println "method1, doing something expensive & slow with $user.name"
}
void method2(User user) {
println "method2, doing something expensive & slow with $user.name"
}
}
class User {
String name
}
现在你可以这样测试:
import spock.lang.Specification
import spock.lang.Unroll
class ATest extends Specification {
@Unroll("user #user.name")
def "expensive calculation (no mocks)"() {
given:
def process = new A()
expect:
process.method(user)
where:
user << [new User(name: "Tom"), new User(name: "Tina")]
}
@Unroll("user #user.name")
def "expensive calculation (spy)"() {
given:
A process = Spy() {
method1(_) >> null
method2(_) >> null
}
expect:
process.method(user)
where:
user << [new User(name: "Tom"), new User(name: "Tina")]
}
}
在第一个测试中,您将看到被调用方法的日志输出,而在第二个测试中您将看不到,因为这两个方法已被删除。默认情况下,如果您不更改间谍程序的行为,间谍程序会调用原始方法。
Leonard 还表示,如果辅助方法所做的事情不是被测试类的核心职责,那么您应该重构。让我们假设,我们这里有这个案例。然后你可以像这样重构和测试:
class B {
ExpensiveCalculation calculation
void method(User user) {
if ("Tom" == user.name)
calculation.method1(user)
else
calculation.method2(user)
}
}
class ExpensiveCalculation {
void method1(User user) {
println "method1, doing something expensive & slow with $user.name"
}
void method2(User user) {
println "method2, doing something expensive & slow with $user.name"
}
}
@Unroll("user #user.name")
def "expensive calculation (factored out, mocked)"() {
given:
def process = new B(calculation: Mock(ExpensiveCalculation))
expect:
process.method(user)
where:
user << [new User(name: "Tom"), new User(name: "Tina")]
}
在 Groovy Web Console 中尝试,然后单击“结果”选项卡查看测试报告。