模拟 BouncyCastle 类 - SecurityException

我正在尝试为使用 BouncyCastle 的类编写单元测试SignerInformation- 我想模拟它的一个实例,但试图这样做会导致java.lang.SecurityException. 这是一个简化的工作示例:


签名者信息消费者.java


import org.bouncycastle.cms.SignerInformation;


public class SignerInformationConsumer {

    public String interact(SignerInformation si) {

        return si.getDigestAlgOID();

    }

}

SignerInformationConsumerTest.groovy


import org.bouncycastle.cms.SignerInformation

import spock.lang.Shared

import spock.lang.Specification


class SignerInformationConsumerTest extends Specification {


    @Shared

    SignerInformation si = Mock()


    def "should return valid array"() {

        given:

            SignerInformationConsumer test = new SignerInformationConsumer()

            si.digestAlgOID >> "aaa"

        when:

            String digest = test.interact(si)

        then:

            digest == "aaa"

    }

}

构建.gradle


plugins {

    id 'java'

}


group 'test'

version '1.0-SNAPSHOT'


sourceCompatibility = 1.8


repositories {

    mavenCentral()

}


dependencies {

    testCompile group: 'org.spockframework', name: 'spock-core', version: '1.1-groovy-2.4'

    testCompile 'net.bytebuddy:byte-buddy:1.8.0'

    compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.60'

}

您能否就如何模拟此类或以不同方式测试行为提出一些解决方案?


慕丝7291255
浏览 168回答 1
1回答

九州编程

您在这里处理签名的 JAR。假设您不想全局禁用 Java 的安全功能,也不想创建所有 BouncyCastle JAR 的副本,删除它们的清单,我将向您展示一个解决方法。问题是 Spock 模拟(也有其他)是在与原始类相同的包中创建的。但是模拟是未签名的代码,因此是错误消息。现在,您可以只对要模拟的类进行子类化并改为模拟该子类。如果您在测试中的任何地方需要它们,只需确保子类具有来自其父类的所有必要构造函数。package de.scrum_master.stackoverflow;import org.bouncycastle.cms.SignerInformation;public class SignerInformationConsumer {  public String interact(SignerInformation si) {    return si.getDigestAlgOID();  }}package de.scrum_master.stackoverflowimport org.bouncycastle.cms.SignerInformationimport spock.lang.Specificationclass SignerInformationConsumerTest extends Specification {  static class SignerInformationMock extends SignerInformation {    protected SignerInformationMock(SignerInformation baseInfo) {      super(baseInfo)    }  }  //SignerInformation signerInformation = Spy(SignerInformationMock, useObjenesis: true)  SignerInformation signerInformation = Mock(SignerInformationMock)  def "should return valid array"() {    given:    SignerInformationConsumer signerInformationConsumer = new SignerInformationConsumer()    signerInformation.getDigestAlgOID() >> "aaa"    expect:    signerInformationConsumer.interact(signerInformation) == "aaa"  }}关于您的测试的几句话:不要使用,@Shared因为测试应该相互独立。您可以将特性方法 A 的副作用转移到 B。共享变量应该只在非常罕见的情况下使用,例如,如果您创建的对象在时间或资源方面非常昂贵。模拟肯定不是。因此,要么在您的功能方法中创建模拟,要么,如果其他功能方法要使用相同的模拟定义,请使用不带@Shared. 当然,您可以忽略此建议,但我仍然认为您应该遵循它。您的测试不测试应用程序,它只测试模拟。我希望你的真实测试用例看起来不同,因为这个测试,即使我让它为你工作,也只检查存根结果是否与测试开始时指定的一样。我的代码中的注释行向您展示了如果出于任何原因需要(例如,您想使用真实对象并仅存根一个或几个方法),您可以如何使用间谍而不是模拟。在这种特殊情况下,您将需要 Objenesis 作为依赖项,否则您将收到异常,因为没有默认构造函数。或者,您必须创建 spy 并包含构造函数参数。如果getDigestAlgOID()要在每个特征方法中以相同的方式存根,您可以将存根部分从特征方法移动到模拟定义中,如下所示:SignerInformation signerInformation = Mock(SignerInformationMock) {  getDigestAlgOID() >> "aaa"}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java