Spock - 返回固定值不按预期工作

Spock - returning fixed value not working as expected

如果我使用的术语不正确,我首先要道歉。我仅将 groovy/java 用于自动化任务 (Gradle),而且我没有多年交付生产级软件的经验。

所以,我面临的挑战如下:我有一个规范试图测试 return 字符串是否符合预期(几乎与 相同)。

def "pretty print returns correct string"() {
    setup:
    X509Certificate stubCert = Stub()
    stubCert.getSubjectDN().toString() >> "Test"

    when:
    def output = X509CertificateUtils.prettyPrint(stubCert)

    then:
    output == "Subject: Test"
}

但是,区别在于我的方法约束 return 是一个 Principal 对象,而它是我真正想要存根的那个对象的 toString() 。我以为我在上面做的是正确的,但它没有给出我期望的结果。

这是我的帮手 class。

import java.security.cert.X509Certificate

class X509CertificateUtils {

    static def prettyPrint(X509Certificate x509Certificate) {
        return "Subject: ${x509Certificate.getSubjectDN()}"
    }
}

如果我 运行 这个测试我得到以下错误:

output == "Subject: Test"
|      |
|      false
|      38 differences (20% similarity)
|      Subject: (Mock for )t(ype 'Principal' named 'dummy')
|      Subject: (Tes------)t(-----------------------------)
Subject: Mock for type 'Principal' named 'dummy'

如有任何帮助,我们将不胜感激。

只需创建第二个存根:

X509Certificate stubCert = Stub()
Principal princ = Stub()
princ.toString() >> "Test"
stubCert.getSubjectDN() >> princ

Spock 有几种伪造对象的方法。这是 current documentation.

  • Stub:一个假对象,return只是它被告知的对象;默认 其他值(0、空等)。
  • Mock:类似于存根,但它 还可以测试对对象进行的方法调用次数。
  • Spy:对象的正常实例化,mocks 被用作侦听器,拦截对对象的调用。这使您可以正常使用该对象,仅更改指定的方法行为。也可以在模拟行为期间的某个时刻调用原始代码。

我的问题是...您是要测试单独使用 prettyPrint() 是否正常工作,SubjectDN.toString() 是否正常打印,或者两者的组合?我的建议是让您的模拟 return 成为您随后测试的实际 SubjectDN() 对象。没有更多的工作,如果出现问题,您可以更好地了解问题的根源。 Max的解决方案将解决您的问题;我没有仔细阅读或遵循良好的测试范围界定实践。我将把其余的答案留作思考。如果您想将 Max 的存根方法与我的参数化混合,我建议将 where 块中的所需字符串传递到设置块中的存根创建。

这就开始跑题了,但是如果你需要测试不止一种 SubjectDN 场景(null、empty、各种大写、数字等);您还应该考虑对测试进行参数化。

def "pretty print returns correct string"() {
    setup:
        X509Certificate stubCert = Mock()
        stubCert.getSubjectDN() >> subject 

    expect:
        subject.toString() == expectedToString
        X509CertificateUtils.prettyPrint(stubCert) == expectedFormatted

    where:
        subject | expectedToString | expectedFormatted
        new SubjectDN("") | ???? | "Subject: ????"
        new SubjectDN(null) | ???? | "Subject: ????"
        new SubjectDN("test") | "test" | "Subject: Test"
        new SubjectDN("testing") | "testing" | "Subject: Testing"
}

如果您的漂亮打印真的像前置 "Subject: " 一样简单,您可能无需计算 expectedFormatted 变量;但是您真的不应该让您的测试模仿您正在测试的代码以试图使测试更容易。

我还发现,当迭代具有流动长度时,table 参数化测试的格式变得混乱或难以维护。我的偏好是制作一个地图列表,每个地图代表一个测试迭代。它将每个测试迭代捆绑在一起,让不熟悉的开发人员更好地了解每个测试迭代需要什么。

@Unroll(iteration.testName) // Shows each iteration in its own result (in most IDEs)
def "testing printing of a SubjectDN"() {
    setup:
        X509Certificate stubCert = Mock()
        stubCert.getSubjectDN() >> iteration.subject 

    expect:
        subject.toString() == iteration.expectedToString
        X509CertificateUtils.prettyPrint(stubCert) == expectedFormatted

    where:
        iteration << [
            [testName: "Testing pretty printing a normal SubjectDN",
             subject: new SubjectDN("test"),
             expectedToString: "test"],

            [testName: "Testing pretty printing a normal SubjectDN",
             subject: new SubjectDN("testing 123"),
             expectedToString: "testing 123"],

            [testName: "Testing pretty printing a null SubjectDN",
             subject: new SubjectDN(null),
             expectedToString: ????] 

            // And so on...             
        ]

        expectedFormatted = "Subject: ${iteration.expectedToString}"
}