Java Spock-返回的固定值未按预期工作

Java Spock-返回的固定值未按预期工作,java,groovy,spock,stub,Java,Groovy,Spock,Stub,如果我使用的术语不正确,我必须首先道歉。我只将groovy/java用于自动化任务(Gradle),而且我没有多年的交付生产级软件的经验 因此,我面临的挑战如下:我有一个规范,它试图测试返回字符串是否符合预期(几乎与相同) 但是,不同之处在于,我的方法约束返回一个主体对象,并且它是我真正想要存根的对象的toString()。我认为我在上面做的是正确的,但它没有给出我期望的结果 这是我的助手课 import java.security.cert.X509Certificate class X50

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

因此,我面临的挑战如下:我有一个规范,它试图测试返回字符串是否符合预期(几乎与相同)

但是,不同之处在于,我的方法约束返回一个主体对象,并且它是我真正想要存根的对象的toString()。我认为我在上面做的是正确的,但它没有给出我期望的结果

这是我的助手课

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

斯波克有几种伪造物品的方法。这是我的建议

  • 存根:只返回它被告知的内容的伪对象;违约 值(0、空等)
  • Mock:类似于存根,但它 还可以测试对对象进行的方法调用数
  • Spy:作为对象的正常实例化,mock作为侦听器应用,侦听对对象的调用。这使您可以正常使用对象,仅更改指定的方法和行为。也可以在模拟行为期间的某个时刻调用原始代码
我想问你的问题。。。您是否试图测试prettyPrint()是否单独正常工作,SubjectDN.toString()是否正常打印,或者两者的组合是否正常工作?我的建议是让mock返回一个实际的SubjectDN()对象,然后再对其进行测试。没有太多的工作,如果出现问题,你会更好地了解问题的根源。马克斯的解决方案会解决你的问题;我没有仔细阅读,也没有遵循良好的测试范围界定实践。我将把剩下的答案留给大家思考。如果您想混合使用Max的存根方法和我的参数化,我建议将where块中所需的字符串传递给setup块中的存根创建

这开始脱离主题,但如果您需要测试多个SubjectDN场景(null、空、各种大写、数字等);您还应该考虑将测试参数化

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"
}
如果你的漂亮打印真的像在“主题”前面加上前缀一样简单:“你可能不需要计算你的expectedFormatted变量;但是你真的不应该让你的测试模仿你正在测试的代码来让测试更容易

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

@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}"
}
@Unroll(iteration.testName)//在自己的结果中显示每个迭代(在大多数IDE中)
def“测试主题DN的打印”(){
设置:
X509Certificate stubCert=Mock()
stubCert.getSubjectDN()>>iteration.subject
期望:
subject.toString()=迭代.expectedToString
X509CertificateUtils.预打印(存根证书)=expectedFormatted
哪里:

迭代谢谢。这解决了我的问题。我有几次接近这个解决方案。出于兴趣,你知道为什么链接方法不起作用吗?链接存根被认为是一种“代码气味”,你可以在这个相关的问题(和第一个答案)中读到更多关于它的信息:。此外,Spock中存根的当前实现并没有被编程为支持存根链接,
a.method()>>b
基本上是一种语法糖,它翻译成类似于
在对象“a”上注册存根,让方法“method”返回“b”
。在您的情况下,Spock首先调用
stubCert.getSubjectDN()
,然后期望结果是存根,并尝试在此返回的对象上注册“toString”。感谢您花时间回答。非常感谢。感谢您的回答。为了简洁起见,我选择使用Max的解决方案,但我确实从您的回答中学到了很多东西。:-)感谢您给我机会赢得足够的评论分数!=)
@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}"
}