.. highlight:: kotlin .. raw:: html API: Testing ============ .. contents:: Flow testing ------------ MockNetwork ^^^^^^^^^^^ Flow testing can be fully automated using a ``MockNetwork`` composed of ``StartedMockNode`` nodes. Each ``StartedMockNode`` behaves like a regular Corda node, but its services are either in-memory or mocked out. A ``MockNetwork`` is created as follows: .. container:: codeset .. sourcecode:: kotlin class FlowTests { private lateinit var mockNet: MockNetwork @Before fun setup() { network = MockNetwork(listOf("my.cordapp.package", "my.other.cordapp.package")) } } .. sourcecode:: java public class IOUFlowTests { private MockNetwork network; @Before public void setup() { network = new MockNetwork(ImmutableList.of("my.cordapp.package", "my.other.cordapp.package")); } } The ``MockNetwork`` requires at a minimum a list of packages. Each package is packaged into a CorDapp JAR and installed as a CorDapp on each ``StartedMockNode``. Configuring the ``MockNetwork`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``MockNetwork`` is configured automatically. You can tweak its configuration using a ``MockNetworkParameters`` object, or by using named parameters in Kotlin: .. container:: codeset .. sourcecode:: kotlin val network = MockNetwork( // A list of packages to scan. Any contracts, flows and Corda services within these // packages will be automatically available to any nodes within the mock network cordappPackages = listOf("my.cordapp.package", "my.other.cordapp.package"), // If true then each node will be run in its own thread. This can result in race conditions in your // code if not carefully written, but is more realistic and may help if you have flows in your app that // do long blocking operations. threadPerNode = false, // The notaries to use on the mock network. By default you get one mock notary and that is usually // sufficient. notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME)), // If true then messages will not be routed from sender to receiver until you use the // [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can // examine the state of the mock network before and after a message is sent, without races and without // the receiving node immediately sending a response. networkSendManuallyPumped = false, // How traffic is allocated in the case where multiple nodes share a single identity, which happens for // notaries in a cluster. You don't normally ever need to change this: it is mostly useful for testing // notary implementations. servicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random()) val network2 = MockNetwork( // A list of packages to scan. Any contracts, flows and Corda services within these // packages will be automatically available to any nodes within the mock network listOf("my.cordapp.package", "my.other.cordapp.package"), MockNetworkParameters( // If true then each node will be run in its own thread. This can result in race conditions in your // code if not carefully written, but is more realistic and may help if you have flows in your app that // do long blocking operations. threadPerNode = false, // The notaries to use on the mock network. By default you get one mock notary and that is usually // sufficient. notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME)), // If true then messages will not be routed from sender to receiver until you use the // [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can // examine the state of the mock network before and after a message is sent, without races and without // the receiving node immediately sending a response. networkSendManuallyPumped = false, // How traffic is allocated in the case where multiple nodes share a single identity, which happens for // notaries in a cluster. You don't normally ever need to change this: it is mostly useful for testing // notary implementations. servicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random()) ) .. sourcecode:: java MockNetwork network = MockNetwork( // A list of packages to scan. Any contracts, flows and Corda services within these // packages will be automatically available to any nodes within the mock network ImmutableList.of("my.cordapp.package", "my.other.cordapp.package"), new MockNetworkParameters() // If true then each node will be run in its own thread. This can result in race conditions in // your code if not carefully written, but is more realistic and may help if you have flows in // your app that do long blocking operations. .setThreadPerNode(false) // The notaries to use on the mock network. By default you get one mock notary and that is // usually sufficient. .setNotarySpecs(ImmutableList.of(new MockNetworkNotarySpec(DUMMY_NOTARY_NAME))) // If true then messages will not be routed from sender to receiver until you use the // [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code // that can examine the state of the mock network before and after a message is sent, without // races and without the receiving node immediately sending a response. .setNetworkSendManuallyPumped(false) // How traffic is allocated in the case where multiple nodes share a single identity, which // happens for notaries in a cluster. You don't normally ever need to change this: it is mostly // useful for testing notary implementations. .setServicePeerAllocationStrategy(new InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random())); Adding nodes to the network ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Nodes are created on the ``MockNetwork`` using: .. container:: codeset .. sourcecode:: kotlin class FlowTests { private lateinit var mockNet: MockNetwork lateinit var nodeA: StartedMockNode lateinit var nodeB: StartedMockNode @Before fun setup() { network = MockNetwork(listOf("my.cordapp.package", "my.other.cordapp.package")) nodeA = network.createPartyNode() // We can optionally give the node a name. nodeB = network.createPartyNode(CordaX500Name("Bank B", "London", "GB")) } } .. sourcecode:: java public class IOUFlowTests { private MockNetwork network; private StartedMockNode a; private StartedMockNode b; @Before public void setup() { network = new MockNetwork(ImmutableList.of("my.cordapp.package", "my.other.cordapp.package")); nodeA = network.createPartyNode(null); // We can optionally give the node a name. nodeB = network.createPartyNode(new CordaX500Name("Bank B", "London", "GB")); } } Nodes added using ``createPartyNode`` are provided a default set of node parameters. However, it is also possible to provide different parameters to each node using the following methods on ``MockNetwork``: .. container:: codeset .. sourcecode:: kotlin /** * Create a started node with the given parameters. * * @param legalName The node's legal name. * @param forcedID A unique identifier for the node. * @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value, * but can be overridden to cause nodes to have stable or colliding identity/service keys. * @param configOverrides Add/override the default configuration/behaviour of the node * @param extraCordappPackages Extra CorDapp packages to add for this node. */ @JvmOverloads fun createNode(legalName: CordaX500Name? = null, forcedID: Int? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), configOverrides: MockNodeConfigOverrides? = null, extraCordappPackages: List = emptyList() ): StartedMockNode /** Create a started node with the given parameters. **/ fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode As you can see above, parameters can be added individually or encapsulated within a ``MockNodeParameters`` object. Of particular interest are ``configOverrides`` which allow you to override some of the default node configuration options. Please refer to the ``MockNodeConfigOverrides`` class for details what can currently be overridden. Also, the ``extraCordappPackages`` parameter allows you to add extra CorDapps to a specific node. This is useful when you wish for all nodes to load a common CorDapp but for a subset of nodes to load CorDapps specific to their role in the network. Running the network ^^^^^^^^^^^^^^^^^^^ When using a ``MockNetwork``, you must be careful to ensure that all the nodes have processed all the relevant messages before making assertions about the result of performing some action. For example, if you start a flow to update the ledger but don't wait until all the nodes involved have processed all the resulting messages, your nodes' vaults may not be in the state you expect. When ``networkSendManuallyPumped`` is set to ``false``, you must manually initiate the processing of received messages. You manually process received messages as follows: * ``StartedMockNode.pumpReceive()`` processes a single message from the node's queue * ``MockNetwork.runNetwork()`` processes all the messages in every node's queue until there are no further messages to process When ``networkSendManuallyPumped`` is set to ``true``, nodes will automatically process the messages they receive. You can block until all messages have been processed using ``MockNetwork.waitQuiescent()``. .. warning:: If ``threadPerNode`` is set to ``true``, ``networkSendManuallyPumped`` must also be set to ``true``. Running flows ^^^^^^^^^^^^^ A ``StartedMockNode`` starts a flow using the ``StartedNodeServices.startFlow`` method. This method returns a future representing the output of running the flow. .. container:: codeset .. sourcecode:: kotlin val signedTransactionFuture = nodeA.services.startFlow(IOUFlow(iouValue = 99, otherParty = nodeBParty)) .. sourcecode:: java CordaFuture future = startFlow(a.getServices(), new ExampleFlow.Initiator(1, nodeBParty)); The network must then be manually run before retrieving the future's value: .. container:: codeset .. sourcecode:: kotlin val signedTransactionFuture = nodeA.services.startFlow(IOUFlow(iouValue = 99, otherParty = nodeBParty)) // Assuming network.networkSendManuallyPumped == false. network.runNetwork() val signedTransaction = future.get(); .. sourcecode:: java CordaFuture future = startFlow(a.getServices(), new ExampleFlow.Initiator(1, nodeBParty)); // Assuming network.networkSendManuallyPumped == false. network.runNetwork(); SignedTransaction signedTransaction = future.get(); Accessing ``StartedMockNode`` internals ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Querying a node's vault ~~~~~~~~~~~~~~~~~~~~~~~ Recorded states can be retrieved from the vault of a ``StartedMockNode`` using: .. container:: codeset .. sourcecode:: kotlin val myStates = nodeA.services.vaultService.queryBy().states .. sourcecode:: java List myStates = node.getServices().getVaultService().queryBy(MyStateType.class).getStates(); This allows you to check whether a given state has (or has not) been stored, and whether it has the correct attributes. Examining a node's transaction storage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Recorded transactions can be retrieved from the transaction storage of a ``StartedMockNode`` using: .. container:: codeset .. sourcecode:: kotlin val transaction = nodeA.services.validatedTransactions.getTransaction(transaction.id) .. sourcecode:: java SignedTransaction transaction = nodeA.getServices().getValidatedTransactions().getTransaction(transaction.getId()) This allows you to check whether a given transaction has (or has not) been stored, and whether it has the correct attributes. This allows you to check whether a given state has (or has not) been stored, and whether it has the correct attributes. Further examples ^^^^^^^^^^^^^^^^ * See the flow testing tutorial :doc:`here ` * See the oracle tutorial :doc:`here ` for information on testing ``@CordaService`` classes * Further examples are available in the Example CorDapp in `Java `_ and `Kotlin `_ Contract testing ---------------- The Corda test framework includes the ability to create a test ledger by calling the ``ledger`` function on an implementation of the ``ServiceHub`` interface. Test identities ^^^^^^^^^^^^^^^ You can create dummy identities to use in test transactions using the ``TestIdentity`` class: .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 14 :end-before: DOCEND 14 :dedent: 8 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 14 :end-before: DOCEND 14 :dedent: 4 ``TestIdentity`` exposes the following fields and methods: .. container:: codeset .. sourcecode:: kotlin val identityParty: Party = bigCorp.party val identityName: CordaX500Name = bigCorp.name val identityPubKey: PublicKey = bigCorp.publicKey val identityKeyPair: KeyPair = bigCorp.keyPair val identityPartyAndCertificate: PartyAndCertificate = bigCorp.identity .. sourcecode:: java Party identityParty = bigCorp.getParty(); CordaX500Name identityName = bigCorp.getName(); PublicKey identityPubKey = bigCorp.getPublicKey(); KeyPair identityKeyPair = bigCorp.getKeyPair(); PartyAndCertificate identityPartyAndCertificate = bigCorp.getIdentity(); You can also create a unique ``TestIdentity`` using the ``fresh`` method: .. container:: codeset .. sourcecode:: kotlin val uniqueTestIdentity: TestIdentity = TestIdentity.fresh("orgName") .. sourcecode:: java TestIdentity uniqueTestIdentity = TestIdentity.Companion.fresh("orgName"); MockServices ^^^^^^^^^^^^ A mock implementation of ``ServiceHub`` is provided in ``MockServices``. This is a minimal ``ServiceHub`` that suffices to test contract logic. It has the ability to insert states into the vault, query the vault, and construct and check transactions. .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 11 :end-before: DOCEND 11 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 11 :end-before: DOCEND 11 :dedent: 4 Alternatively, there is a helper constructor which just accepts a list of ``TestIdentity``. The first identity provided is the identity of the node whose ``ServiceHub`` is being mocked, and any subsequent identities are identities that the node knows about. Only the calling package is scanned for cordapps and a test ``IdentityService`` is created for you, using all the given identities. .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 12 :end-before: DOCEND 12 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 12 :end-before: DOCEND 12 :dedent: 4 Writing tests using a test ledger ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``ServiceHub.ledger`` extension function allows you to create a test ledger. Within the ledger wrapper you can create transactions using the ``transaction`` function. Within a transaction you can define the ``input`` and ``output`` states for the transaction, alongside any commands that are being executed, the ``timeWindow`` in which the transaction has been executed, and any ``attachments``, as shown in this example test: .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 13 :end-before: DOCEND 13 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 13 :end-before: DOCEND 13 :dedent: 4 Once all the transaction components have been specified, you can run ``verifies()`` to check that the given transaction is valid. Checking for failure states ~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to test for failures, you can use the ``failsWith`` method, or in Kotlin the ``fails with`` helper method, which assert that the transaction fails with a specific error. If you just want to assert that the transaction has failed without verifying the message, there is also a ``fails`` method. .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 4 :end-before: DOCEND 4 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 4 :end-before: DOCEND 4 :dedent: 4 .. note:: The transaction DSL forces the last line of the test to be either a ``verifies`` or ``fails with`` statement. Testing multiple scenarios at once ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Within a single transaction block, you can assert several times that the transaction constructed so far either passes or fails verification. For example, you could test that a contract fails to verify because it has no output states, and then add the relevant output state and check that the contract verifies successfully, as in the following example: .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 5 :end-before: DOCEND 5 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 5 :end-before: DOCEND 5 :dedent: 4 You can also use the ``tweak`` function to create a locally scoped transaction that you can make changes to and then return to the original, unmodified transaction. As in the following example: .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 7 :end-before: DOCEND 7 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 7 :end-before: DOCEND 7 :dedent: 4 Chaining transactions ~~~~~~~~~~~~~~~~~~~~~ The following example shows that within a ``ledger``, you can create more than one ``transaction`` in order to test chains of transactions. In addition to ``transaction``, ``unverifiedTransaction`` can be used, as in the example below, to create transactions on the ledger without verifying them, for pre-populating the ledger with existing data. When chaining transactions, it is important to note that even though a ``transaction`` ``verifies`` successfully, the overall ledger may not be valid. This can be verified separately by placing a ``verifies`` or ``fails`` statement within the ``ledger`` block. .. container:: codeset .. literalinclude:: ../../docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt :language: kotlin :start-after: DOCSTART 9 :end-before: DOCEND 9 :dedent: 4 .. literalinclude:: ../../docs/source/example-code/src/test/java/net/corda/docs/java/tutorial/testdsl/TutorialTestDSL.java :language: java :start-after: DOCSTART 9 :end-before: DOCEND 9 :dedent: 4 Further examples ^^^^^^^^^^^^^^^^ * See the flow testing tutorial :doc:`here ` * Further examples are available in the Example CorDapp in `Java `_ and `Kotlin `_