Understanding Contract Testing in Corda

Faris Alblooki
Corda
Published in
3 min readMay 29, 2024

--

When working with Corda, you will encounter three main components: State, Contract, and Flow. The State represents the data structure of your application; the Contract defines the rules for transaction validity, ensuring that only transactions meeting specific criteria are allowed; and the Flow manages the transaction process, orchestrating steps from proposal to finality across different network participants. Now, let’s focus on how to effectively test the contracts in a Cordapp.

What is contract testing?

In Corda 5, contract testing is a specialized flow within the workflow package that functions similarly to other flows but is designed specifically for Cordapp Contract testing. This flow does not require a signature and does not produce any output state to the vault, ensuring it doesn’t alter the ledger during tests. The primary purpose of this flow is to validate the rules set within your contract by simulating transactions that should either pass or fail based on those rules.

Example code

How you can implement robust contract testing in Corda:

Java Code Snippet Example: Testing Multiple Commands Method

For the code in Kotlin, refer to this GitHub Gist. For the code in Java, refer to this GitHub Gist.

Dive into the Code

State Creation

The initialization of the transaction also involves creating a state, an essential first action that sets the stage for testing the contract rules:

For the code in Java, refer to this GitHub Gist. For the code in Kotlin, refer to this GitHub Gist

Setup Transaction

Next, we are initializing a TransactionBuilder which sets the groundwork for a valid transaction. This builder specifies the notary, time window, output state, command, and signatories essential for the transaction:

The sample code provided above illustrates a test scenario for validating the transaction’s adherence to contract stipulations that restrict it to a single command. In this instance, the txBuilder is deliberately configured with two addCommand calls, which contravenes the contract's requirement for only one command per transaction.

For the code in Java, refer to this GitHub Gist. For the code in Kotlin, refer to this GitHub Gist.

Error Handling

It’s essential to catch exceptions and log errors to understand why a test or transaction setup might fail, helping identify issues in the contract logic.

Returning the Test Result

Consequently, executing this test should result in an error, specifically indicating “Require a single command.” This is intended to verify the robustness of the contract’s enforcement mechanisms, ensuring that transactions not conforming to this critical rule are reliably rejected. This is handled at the after the transaction has been attempted as shown here:

For the code in Java, refer to this GitHub Gist. For the code in Kotlin, refer to this GitHub Gist.

More Example Tests

Areas of Testing

Each test method assesses specific contract rules such as the number of commands or the structure of transaction inputs and outputs. The results, labeled as “Pass” or “Fail”, are returned and stored for a clear and actionable overview.

Some of example tests that you can think of:

  • Multiple Commands Not Permitted: Ensures a transaction cannot have more than one command type.
  • Only Two Participants: Checks that only two parties are involved in any transaction, similar to an IOU scenario.
  • Settle Transactions: Validates that transactions resolving obligations (settle) have exactly one input and one output.
  • Transfer Transactions: Ensures that obligations are transferred cleanly with one input and one output.
  • Issue Transactions: Confirms that each new IOU is uniquely represented with a single output.

Additional code example

Only two participants

The sample code above presents a test scenario designed to verify that a transaction includes precisely two participants, as required by the contract. In this example, the state is purposely created with only one participant in line 10 then fed into the transaction as the output state, violating the contract’s stipulation that each state in a transaction has to contain two participants.

For the code in Java, refer to this GitHub Gist. For the code in Kotlin, refer to this GitHub Gist.

Conclusion

By breaking down and testing each rule encapsulated within your contract, you ensure that your Cordapp functions securely and as expected. Implementing these tests is not just about checking the box but ensuring the integrity and reliability of your blockchain application.

For the code and files in Java, refer to this GitHub Project. For the files in Kotlin, refer to this GitHub Project.

Written by Faris Alblooki. A Developer Evangelist at R3.
If there are any questions reach out to devrel@r3.com.

--

--