Test Driven Development for Blockchain Apps with R3 Corda — How to Write Contracts and Unit Tests
Tags: Blockchain, Corda, Programming, Tdd, Tutorial

What about the unit tests!?

Now we’re ready to start implementing TDD and developing out our Contract specifications.

The Template project already has an IntelliJ Run Configuration set up for running Contract, Flow, and Integration tests. Right now we have a dummy contract test defined in ContractTests.java (cordapp/src/test/..), so let’s go ahead and try it out:

And we should see:

Preparing for a Unit Test

Corda has a built-in MockServices interface that makes it very easy to write Contract tests. Using the “ledger” method, our mock services will provide a clean slate each test with no pre-existing transaction or services. This way we can be sure each test is isolated with no side effects.

We’ll also utilize a TestIdentity, which will be a mocked node— or participating party in the blockchain. When instantiating the TestIdentity, we’ll use a CordaX500Name, specifying the name of the individual or organization, as well as the country and locality. These values can be whatever you want. The identity might represent a bank, a company, an individual, or any other entity that would be a participant in your blockchain app.

ContractTests.java

cordapp/src/test/java/[package]

How to Use the MockServices

Let’s discuss the use of the ledger() method. See below:

(Edit: If IntelliJ can’t autoimport ledger, you can manually add it via “import static net.corda.testing.node.NodeTestUtils.ledger;”).

We call ledger, providing our instantiated MockServices object which we’ve named ledgerServices. Then we have a predicate expression ledger -> {} . Everything inside of these brackets will be in our isolated blank-slate mock ledger. Within our ledger expression, we will create a new transaction using the method ledger.transaction(tx -> {}); . Everything inside of these second set of brackets will be corresponding to the new transaction, referenced by variable tx. For now, we don’t care about the return values, so we’ll return null from both expressions.

Add a Unit Test

Now let’s get rid of that dummyTest and replace it with something legitimate.

Let’s say that for now the ledger only supports adding a bank account and balance entry, but not altering or removing it. In this case, our transactions will produce one output state — a TemplateState entry that contains an account number and balance — and consumes NO input states. So our contract will need to make sure that a transaction does not include an input state if it is to be approved as valid.

Let’s add our first unit test, which will verify that an exception is thrown if a transaction is issued which contains an input state.

Let’s run the unit test!

And…

1 test failed. 😢 😢 😢

Uh oh..

Just kidding, that’s what we want, because we haven’t written any code in our Contract class to throw the exception the test is checking for!

java.lang.AssertionError: Expected exception but didn’t get one 👍

This is the TDD philosophy. Test first, code second. We should be able to make this test pass without making any changes to the test itself.

Note: If you get an error saying “Constructor parameter — ‘arg0’ — doesn’t refer to a property of …”, no big deal! You just need to add the following lines to your .idea/compiler.xml file: <component name="JavacSettings">

<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />

</component> If you still have issues, try rebuilding, gradle clean, restarting IntelliJ, etc. to make sure nothing is cached.

Write Contract Code

Let’s add some code into our contract to prevent ledger updates from being approved if the transactions has any inputs states.

Using the requireThat() method, we can have any number of require.using() clauses — each of which takes a string to be used for the exception message, and an expression that will throw the exception if evaluated to true. Here, we grab the list of inputs from our transaction by calling getInputs() on our injected transaction variable tx, and then check to see if it’s empty.

Now when we re-run our unit test..

It passes! 👍 💯 😍

Continue the cycle => write failing test, make it pass

Let’s add a few more Contract unit tests for good measure:

Run the tests..

😨

Looks scary, but you’ll get used to it. 😅

The new tests fail. Add the contract clauses to make them pass:

Note that on Line 8 we grab the first (any only) output state from our transaction object tx and cast it to a TemplateState so that we can check its properties.

And re-running tests…

All passing now!

💯 👍 😄