Corda-测试与';至少';一个输出

Corda-测试与';至少';一个输出,corda,Corda,我正在尝试测试一个契约命令,其中的规则是: "Zero input states must be consumed" using (tx.inputs.isEmpty()) "At least one output must be created" using (tx.outputs.isNotEmpty()) 我如何准确地测试“必须至少创建一个输出” 如果我使用一个输入进行测试,则测试无法通过第一次验证(输入必须为零) 如果使用零输入/输出进行测试,则测试失败,因为事务必须使用输入或生成输

我正在尝试测试一个契约命令,其中的规则是:

"Zero input states must be consumed" using (tx.inputs.isEmpty())
"At least one output must be created" using (tx.outputs.isNotEmpty())
我如何准确地测试“必须至少创建一个输出”

  • 如果我使用一个输入进行测试,则测试无法通过第一次验证(输入必须为零)
  • 如果使用零输入/输出进行测试,则测试失败,因为事务必须使用输入或生成输出
  • 如果我使用一个引用状态进行测试,它似乎不起作用,这表明异常是预期的,但没有得到

如果在没有输入和输出的情况下运行测试,则在运行合同代码之前会出现错误:

java.lang.AssertionError: Expected exception containing 'At least one output must be created' but raised exception was 'java.lang.IllegalStateException: A transaction must contain at least one input or output state'
你的问题的第二点是什么

如果使用零输入/输出进行测试,则测试失败,因为事务必须使用输入或生成输出

这意味着您实际上无法以检查特定消息的方式测试此代码

e、 g.与

this `fails with` "At least one output must be created" 
本质上,您的契约代码检查至少一个输出状态是多余的,而您的代码检查零输入状态

但是,您可以编写测试以期望出现
非法状态异常

这样,如果您的代码更改,并且输入随后变为有效,您将有一个失败的测试,该测试将向您发出警告

检查合同代码中至少一个输出状态的替代方法是检查特定类型的至少一个状态

e、 g


关于检查特定
ContractState
类型的最后一点,这正是它所做的(尽管我在问题中的示例没有说明这一点)。我也可以创建一个输出
DummyState
的测试,因此我的契约应该测试特定类型的状态,但是由于仍然没有该状态类型的输入和输出,它似乎忽略了检查该命令,可能是因为在事务中没有绑定到该契约的输入/输出,所以不需要这样做。那么您必须执行问题中未指定的其他操作。我已经用一个演示示例更新了答案,正如所解释的那样。@MatthewLayton我也遇到了你同样的问题,答案代码中的技巧(很难注意)是在你的合同ID中使用一个虚拟状态(注意他使用了
demo\u contract\u ID
而不是
DummyContract.PROGRAM\u ID
);这将使您的演示合约运行并引发异常。
"Output states must be Demo States." using (tx.outputsOfType<DemoState>().isNotEmpty())
class DemoContract : Contract {
    companion object {
        @JvmStatic
        val DEMO_CONTRACT_ID: String = DemoContract::class.java.name
    }

    interface Commands : CommandData {
        class Issue : TypeOnlyCommandData(), Commands
    }

    override fun verify(tx: LedgerTransaction) {
        val command = tx.commands.requireSingleCommand<Commands>()
        val signers = command.signers.toSet()

        when (command.value) {
            is Commands.Issue -> verifyIssue(tx, signers)
            else -> throw IllegalArgumentException("Unrecognised command.")
        }
    }

    private fun verifyIssue(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat {
        "No inputs are required when creating a new demo." using (tx.inputStates.isEmpty())
        "Output states must be Demo States." using (tx.outputsOfType<DemoState>().isNotEmpty())
    }

}
class DemoContractTests {
    private val owner = TestIdentity(CordaX500Name("Series", "London", "GB"))
    private val borrower = TestIdentity(CordaX500Name("One", "London", "GB"))
    private val mockServices = MockServices(owner)

    @Test
    fun `Demo issue must have no inputs`() {
        mockServices.ledger {
            transaction {
                input(DEMO_CONTRACT_ID, DummyState())
                command(listOf(owner.publicKey, borrower.publicKey), DemoContract.Commands.Issue())
                this `fails with` "No inputs are required when creating a new Demo."
            }
        }
    }

    @Test
    fun `There must only be DemoState outputs`() {
        mockServices.ledger {
            transaction {
                output(DEMO_CONTRACT_ID, DummyState(0))
                command(listOf(owner.publicKey, borrower.publicKey), DemoContract.Commands.Issue())
                this `fails with` "Output states must be Demo States."
            }
        }
    }

}