You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对状态包含StatePointer的Contract进行单元测试?MockServices调用vaultService报错解决方案

Solution for Testing Corda Contracts with LinearPointer References

I’ve run into this exact issue when testing contracts with LinearPointer references in Corda, so I can walk you through two solid solutions depending on your contract’s verification logic:

Option 1: Test Pointer Rules Directly (Avoid Resolving)

If your contract’s verification logic only needs to validate the pointer’s metadata (like target state type or pointerId), you don’t need to actually resolve the pointed-to state. This avoids the MockServices vault limitation entirely.

Example Contract Verification Logic

override fun verify(tx: LedgerTransaction) {
    val outputState = tx.outputsOfType<YourStateWithPointer>().single()
    // Validate the pointer targets the correct state type
    require(outputState.pointer.stateType == TargetState::class.java) {
        "Pointer must reference a TargetState"
    }
    // Validate the pointer points to a valid linear ID (if needed)
    require(outputState.pointer.pointerId is UniqueIdentifier) {
        "Pointer must use a UniqueIdentifier"
    }
}

Corresponding Test

@Test
fun `validates pointer targets correct state type`() {
    val notary = MockServices.getMockNotary()
    val invalidPointerState = YourStateWithPointer(
        pointer = LinearPointer(WrongState::class.java, UniqueIdentifier())
    )

    val txBuilder = TransactionBuilder(notary)
        .addOutputState(invalidPointerState)
        .addCommand(YourContract.Commands.Create(), listOf(alice.owningKey))

    // Expect failure due to wrong pointer type
    assertThrows<IllegalArgumentException> {
        txBuilder.toLedgerTransaction(MockServices(listOf("your.contract.package"))).verify()
    }
}

Option 2: Customize MockServices to Support State Resolution

If your contract must resolve the pointed-to state to validate its contents (e.g., checking a field in the target state), you’ll need to extend MockServices with a functional VaultService that can return your pre-defined test states.

Step 1: Create a Mock Vault Service

class TestVaultService(private val testStates: Map<StateRef, ContractState>) : VaultService {
    // Implement only the methods needed for resolution; others can throw exceptions
    override fun queryBy(
        criteria: QueryCriteria,
        paging: PageSpecification,
        sorting: Sort,
        includeSoftDeleted: Boolean
    ): Page<Vault.State> {
        // Map our test states to Vault.State instances (simplified for testing)
        val vaultStates = testStates.map { (ref, state) ->
            Vault.State(
                state = state,
                ref = ref,
                status = Vault.StateStatus.UNCONSUMED,
                contractState = state,
                recordedTime = Instant.now(),
                consumedTime = null,
                lockId = null,
                lockUpdateTime = null
            )
        }
        return Page(vaultStates, paging, vaultStates.size.toLong())
    }

    // Default implementations for unused methods
    override fun trackBy(
        criteria: QueryCriteria,
        paging: PageSpecification,
        sorting: Sort,
        includeSoftDeleted: Boolean
    ): DataFeed<Page<Vault.State>, Vault.Update> {
        throw UnsupportedOperationException()
    }

    override fun softDelete(states: List<StateRef>, reason: String) = throw UnsupportedOperationException()
    // Add other required VaultService methods with default UnsupportedOperationException implementations
}

Step 2: Extend MockServices with Your Vault

class MockServicesWithVault(testStates: Map<StateRef, ContractState>) : MockServices(listOf("your.contract.package")) {
    private val customVault = TestVaultService(testStates)

    override val vaultService: VaultService
        get() = customVault
}

Step 3: Use the Custom MockServices in Tests

@Test
fun `validates pointed-to state meets requirements`() {
    val notary = MockServices.getMockNotary()
    // Create the target state we want our pointer to reference
    val targetState = TargetState(requiredField = "validValue", linearId = UniqueIdentifier())
    val targetStateRef = StateRef(TxHash.random(), 0)
    // Map the state ref to our target state for the mock vault
    val testStatesMap = mapOf(targetStateRef to targetState)

    // Initialize our custom MockServices with the test state
    val mockServices = MockServicesWithVault(testStatesMap)

    // Create our state with a valid pointer to the target state
    val validState = YourStateWithPointer(
        pointer = LinearPointer(TargetState::class.java, targetState.linearId)
    )

    val txBuilder = TransactionBuilder(mockServices.networkParameters.notary)
        .addOutputState(validState)
        .addCommand(YourContract.Commands.Create(), listOf(alice.owningKey))

    // The pointer will now resolve successfully using our mock vault
    txBuilder.toLedgerTransaction(mockServices).verify()
}

Key Notes

  • When extending MockServices, make sure to include your contract’s package in the constructor arguments so Corda can find your contract classes.
  • Keep the TestVaultService implementation minimal—only implement the methods your contract’s resolution logic actually uses.

内容的提问来源于stack exchange,提问作者McXD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 07:02:41