如何在同一CorDapp中测试两种响应者流程及修复测试报错
解决同一CorDapp中多响应流程的测试报错问题
嘿,这个IllegalArgumentException我太熟了——核心原因就是你在同一个CorDapp里给两个响应流程都加了@InitiatedBy(Initiator::class)注解,Corda节点的流程注册器直接懵了:到底用哪个流程处理发起方的请求?再加上测试环节没明确指定节点和响应流程的对应关系,可不就报错了嘛。
下面分两步给你搞定,先把流程逻辑调对,再把测试配置做好:
一、先把流程的注解和发起逻辑捋顺
首先得去掉多个响应流程共用同一个@InitiatedBy的冲突,改成让发起方明确告诉每个交易对手该启动哪个响应流程:
发起方Initiator流程:
保留@InitiatingFlow和@StartableByRPC注解,关键是在和Alice、Bob建立会话时,用initiateFlow的重载方法指定对方要启动的流程类,别让节点瞎猜:@InitiatingFlow @StartableByRPC class Initiator(private val aliceParty: Party, private val bobParty: Party) : FlowLogic<SignedTransaction>() { override fun call(): SignedTransaction { // 明确告诉Alice节点:启动AliceFlow来响应我 val aliceSession = initiateFlow(aliceParty, flowClass = AliceFlow::class.java) // 同理告诉Bob节点启动BobFlow val bobSession = initiateFlow(bobParty, flowClass = BobFlow::class.java) // 后面就是常规的交易构建、签名逻辑了 val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first()) // 这里填充你的交易内容,比如状态、命令啥的 val partSignedTx = serviceHub.signInitialTransaction(txBuilder) // 收集Alice和Bob的签名 val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, listOf(aliceSession, bobSession))) return subFlow(FinalityFlow(fullySignedTx, listOf(aliceSession, bobSession))) } }AliceFlow和BobFlow响应流程:
直接把@InitiatedBy(Initiator::class)注解删掉,改成普通的FlowLogic子类,接收发起方的会话就行。注意加个无参构造,不然测试框架会报错:class AliceFlow(private val otherSession: FlowSession) : FlowLogic<SignedTransaction>() { // 测试用的无参构造,必须加 @Suppress("unused") constructor() : this(FlowSessionImpl()) override fun call(): SignedTransaction { // 这里写Alice的响应逻辑,比如校验交易、签名 val signTxFlow = object : SignTransactionFlow(otherSession) { override fun checkTransaction(stx: SignedTransaction) { // 自定义校验逻辑,比如检查交易内容是否合法 } } val txId = subFlow(signTxFlow) return subFlow(ReceiveFinalityFlow(otherSession, expectedTxId = txId.id)) } }BobFlow的结构和AliceFlow完全一样,只需要替换类名和校验逻辑就行。
二、配置测试用的MockNetwork
用Corda的MockNetwork测试时,得给每个节点注册所有需要的流程,确保节点知道该处理哪些流程:
class MultiResponderFlowTest { private lateinit var mockNetwork: MockNetwork private lateinit var initiatorNode: StartedNode<MockNode> private lateinit var aliceNode: StartedNode<MockNode> private lateinit var bobNode: StartedNode<MockNode> @Before fun setup() { // 初始化MockNetwork,加载你的CorDapp mockNetwork = MockNetwork(MockNetworkParameters(cordappsForAllNodes = listOf( TestCordapp.findCordapp("com.template") ))) // 创建三个测试节点 initiatorNode = mockNetwork.createNode() aliceNode = mockNetwork.createNode() bobNode = mockNetwork.createNode() // 给每个节点注册两个响应流程,避免节点找不到流程 listOf(initiatorNode, aliceNode, bobNode).forEach { node -> node.registerInitiatedFlow(AliceFlow::class.java) node.registerInitiatedFlow(BobFlow::class.java) } // 启动网络 mockNetwork.runNetwork() } @After fun tearDown() { // 测试结束后停止所有节点 mockNetwork.stopNodes() } @Test fun `test multi responder flow execution`() { val aliceParty = aliceNode.info.legalIdentities.first() val bobParty = bobNode.info.legalIdentities.first() // 启动发起方流程 val flow = Initiator(aliceParty, bobParty) val future = initiatorNode.startFlow(flow) // 运行网络直到所有流程执行完 mockNetwork.runNetwork() // 验证交易结果,比如检查交易输出是否存在 val signedTx = future.get() assert(signedTx.tx.outputs.isNotEmpty()) // 这里可以加更多自定义验证逻辑,比如检查签名数量 } }
几个关键注意点
- 如果你的Corda版本比较老,
initiateFlow的flowClass参数可能不存在,那可以改用SendAndReceiveFlow传递自定义消息,让响应流程根据消息类型判断是否处理。 - 要是你非要保留
@InitiatedBy注解(比如需要支持RPC启动响应流程),那可以在响应流程里加身份校验:只有当发起方是预期的节点时才处理,否则抛异常,比如:@InitiatedBy(Initiator::class) class AliceFlow(private val otherSession: FlowSession) : FlowLogic<SignedTransaction>() { override fun call(): SignedTransaction { // 只处理来自Initiator节点的请求 val initiatorParty = initiatorNode.info.legalIdentities.first() if (otherSession.counterparty != initiatorParty) { throw IllegalArgumentException("这个流程只响应来自${initiatorParty.name}的Initiator请求") } // 后续逻辑... } }
内容的提问来源于stack exchange,提问作者Joel
相关产品推荐
相关产品推荐

