corda/docs/source/flow-testing.rst
Anthony Keenan d77290831c CORDA-1035 - Testing api KDoc Updates (#2670)
* [CORDA-1035] Testing api KDoc Updates (#2584)

* Testing api KDoc Updates

* Update after code review

* Update api-current

* Revert changes to compiler.xml

* Made comment changes from review

* Fixing merge conflict

* Don't expose net.corda.node through test API (first pass)

* Fixing merge conflicts

* Update api-current

* Addressing review commits

* Fix exposure of internal implementation of MessageHandlerRegistration

* Make InProcess expose ServiceHub instead of internal StartedNodeServices

* Move InternalMockMessaginService interface to internal namespace

* Move MOCK_VERSION_INFO to internal namespace to avoid exposing VersionInfo

* Don't expose WritableTransactionStorage via testing api

* Create public VerifierType enum

* Update api-current and modify check-api-changes to check for net.corda.node exposures

* Fix merge conflicts

* Fixing another merge conflict

* Fix accidentally broken unit tests

* Make getInternalServices a property

* Fix failing unit tests

* Add todo to check-api-changes

* Fix rpc sender thread busy looping

* Fix tests

* Fixing tests

* Address mike's comments

* Fixing tests

* Make random port allocation internal

* Update api

* Update api
2018-02-28 15:59:56 +00:00

4.3 KiB

Writing flow tests

A flow can be a fairly complex thing that interacts with many services and other parties over the network. That means unit testing one requires some infrastructure to provide lightweight mock implementations. The MockNetwork provides this testing infrastructure layer; you can find this class in the test-utils module.

A good example to examine for learning how to unit test flows is the ResolveTransactionsFlow tests. This flow takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start with this basic skeleton:

../../core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt

We create a mock network in our @Before setup method and create a couple of nodes. We also record the identity of the notary in our test network, which will come in handy later. We also tidy up when we're done.

Next, we write a test case:

../../core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt

We'll take a look at the makeTransactions function in a moment. For now, it's enough to know that it returns two SignedTransaction objects, the second of which spends the first. Both transactions are known by MegaCorpNode but not MiniCorpNode.

The test logic is simple enough: we create the flow, giving it MegaCorpNode's identity as the target to talk to. Then we start it on MiniCorpNode and use the mockNet.runNetwork() method to bounce messages around until things have settled (i.e. there are no more messages waiting to be delivered). All this is done using an in memory message routing implementation that is fast to initialise and use. Finally, we obtain the result of the flow and do some tests on it. We also check the contents of MiniCorpNode's database to see that the flow had the intended effect on the node's persistent state.

Here's what makeTransactions looks like:

../../core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt

We're using the DummyContract, a simple test smart contract which stores a single number in its states, along with ownership and issuer information. You can issue such states, exit them and re-assign ownership (move them). It doesn't do anything else. This code simply creates a transaction that issues a dummy state (the issuer is MEGA_CORP, a pre-defined unit test identity), signs it with the test notary and MegaCorp keys and then converts the builder to the final SignedTransaction. It then does so again, but this time instead of issuing it re-assigns ownership instead. The chain of two transactions is finally committed to MegaCorpNode by sending them directly to the megaCorpNode.services.recordTransaction method (note that this method doesn't check the transactions are valid) inside a database.transaction. All node flows run within a database transaction in the nodes themselves, but any time we need to use the database directly from a unit test, you need to provide a database transaction as shown here.

With regards to initiated flows (see flow-state-machines for information on initiated and initiating flows), the full node automatically registers them by scanning the CorDapp jars. In a unit test environment this is not possible so MockNode has the registerInitiatedFlow method to manually register an initiated flow.