groovy 如何使用Spock跳过一些内部void方法

rbpvctlc  于 2023-10-15  发布在  其他
关注(0)|答案(1)|浏览(181)

验证码:

public class A{
    public void method(User user){
        String name = user.getName();
        if("Tom".equals(name)){
            method1(user);
        }else{
            method2(user);
        }
    }
}

我想用spock写一个关于A#方法()的测试。正如你所看到的,当我测试方法()时,我并不关心方法1()或方法2()的执行,我只是想验证这两个方法在某些情况下会被调用。我的测试代码:

@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()实际执行,而只是调用。我该怎么办?
ps.我也用spy:

@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()。

cnjp1d6j

cnjp1d6j1#

伦纳德说得很对:>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()

    when:
    process.method(user)

    then:
    1 * process."$calledMethod"(user) >> null

    where:
    user                   | calledMethod
    new User(name: 'Tom')  | 'method1'
    new User(name: 'Tina') | 'method2'
  }

在第一个测试中,您将看到被调用方法的日志输出,而在第二个测试中,您将看不到,因为这两个方法已被清除。默认情况下,如果您不更改其行为,间谍将调用原始方法。
伦纳德还说,如果helper方法做的事情不是被测试类的核心职责,那么就应该重构。我们假设,我们有这个案子。然后你可以像这样重构和测试:

class B {
  ExpensiveCalculation calculation

  void method(User user) {
    if ('Tom' == user.name)
      this.calculation.method1(user)
    else
      this.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 calculation = Mock(ExpensiveCalculation)
    def process = new B(calculation: calculation)

    when:
    process.method(user)

    then:
    1 * calculation."$calledMethod"(user)

    where:
    user                   | calledMethod
    new User(name: 'Tom')  | 'method1'
    new User(name: 'Tina') | 'method2'
  }

看看注入一个mock并验证其行为是多么容易?没有更多的丑陋的自我监视类下测试,但明确分离的关注。但是在你使用交互测试(验证行为)之前,想想你是否需要它。仅仅因为它是可能的,并不意味着它总是必要的。
Groovy Web Console中尝试并单击“结果”选项卡以查看测试报告。

相关问题