CORDA-2345: Updated docs to use the new TestCordapp API, rather than the old scan cordapp packages (#4491)

Also made some improvements to the API, especially for Java users.
This commit is contained in:
Shams Asari 2019-01-03 17:57:28 +00:00 committed by GitHub
parent ad1a96fefb
commit f590300cdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 143 additions and 347 deletions

View File

@ -311,70 +311,6 @@ For example:
For Contracts that are annotated with ``@NoConstraintPropagation``, the platform requires that the Transaction Builder specifies For Contracts that are annotated with ``@NoConstraintPropagation``, the platform requires that the Transaction Builder specifies
an actual constraint for the output states (the ``AutomaticPlaceholderConstraint`` can't be used) . an actual constraint for the output states (the ``AutomaticPlaceholderConstraint`` can't be used) .
Testing
-------
Since all tests involving transactions now require attachments it is also required to load the correct attachments
for tests. Unit test environments in JVM ecosystems tend to use class directories rather than JARs, and so CorDapp JARs
typically aren't built for testing. Requiring this would add significant complexity to the build systems of Corda
and CorDapps, so the test suite has a set of convenient functions to generate CorDapps from package names or
to specify JAR URLs in the case that the CorDapp(s) involved in testing already exist. You can also just use
``AlwaysAcceptAttachmentConstraint`` in your tests to disable the constraints mechanism.
MockNetwork/MockNode
********************
The simplest way to ensure that a vanilla instance of a MockNode generates the correct CorDapps is to use the
``cordappPackages`` constructor parameter (Kotlin) or the ``setCordappPackages`` method on ``MockNetworkParameters`` (Java)
when creating the MockNetwork. This will cause the ``AbstractNode`` to use the named packages as sources for CorDapps. All files
within those packages will be zipped into a JAR and added to the attachment store and loaded as CorDapps by the
``CordappLoader``.
An example of this usage would be:
.. sourcecode:: java
class SomeTestClass {
MockNetwork network = null;
@Before
void setup() {
network = new MockNetwork(new MockNetworkParameters().setCordappPackages(Arrays.asList("com.domain.cordapp")))
}
... // Your tests go here
}
MockServices
************
If your test uses a ``MockServices`` directly you can instantiate it using a constructor that takes a list of packages
to use as CorDapps using the ``cordappPackages`` parameter.
.. sourcecode:: java
MockServices mockServices = new MockServices(Arrays.asList("com.domain.cordapp"))
However - there is an easier way! If your unit tests are in the same package as the contract code itself, then you
can use the no-args constructor of ``MockServices``. The package to be scanned for CorDapps will be the same as the
the package of the class that constructed the object. This is a convenient default.
Driver
******
The driver takes a parameter called ``extraCordappPackagesToScan`` which is a list of packages to use as CorDapps.
.. sourcecode:: java
driver(new DriverParameters().setExtraCordappPackagesToScan(Arrays.asList("com.domain.cordapp"))) ...
Full Nodes
**********
When testing against full nodes simply place your CorDapp into the cordapps directory of the node.
Debugging Debugging
--------- ---------
If an attachment constraint cannot be resolved, a ``MissingContractAttachments`` exception is thrown. There are two If an attachment constraint cannot be resolved, a ``MissingContractAttachments`` exception is thrown. There are two

View File

@ -22,108 +22,20 @@ A ``MockNetwork`` is created as follows:
.. container:: codeset .. container:: codeset
.. sourcecode:: kotlin .. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/MockNetworkTestsTutorial.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
class FlowTests { .. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/java/MockNetworkTestsTutorial.java
private lateinit var mockNet: MockNetwork :language: java
:start-after: DOCSTART 1
:end-before: DOCEND 1
@Before The ``MockNetwork`` requires at a minimum a list of CorDapps to be installed on each ``StartedMockNode``. The CorDapps are looked up on the
fun setup() { classpath by package name, using ``TestCordapp.findCordapp``.
network = MockNetwork(listOf("my.cordapp.package", "my.other.cordapp.package"))
}
}
``MockNetworkParameters`` provides other properties for the network which can be tweaked. They default to sensible values if not specified.
.. 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 Adding nodes to the network
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -132,73 +44,21 @@ Nodes are created on the ``MockNetwork`` using:
.. container:: codeset .. container:: codeset
.. sourcecode:: kotlin .. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/MockNetworkTestsTutorial.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCEND 2
class FlowTests { .. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/java/MockNetworkTestsTutorial.java
private lateinit var mockNet: MockNetwork :language: java
lateinit var nodeA: StartedMockNode :start-after: DOCSTART 2
lateinit var nodeB: StartedMockNode :end-before: DOCEND 2
@Before Nodes added using ``createNode`` are provided a default set of node parameters. However, it is also possible to
fun setup() { provide different parameters to each node using ``MockNodeParameters``. Of particular interest are ``configOverrides`` which allow you to
network = MockNetwork(listOf("my.cordapp.package", "my.other.cordapp.package")) override some of the default node configuration options. Please refer to the ``MockNodeConfigOverrides`` class for details what can currently
nodeA = network.createPartyNode() be overridden. Also, the ``additionalCordapps`` parameter allows you to add extra CorDapps to a specific node. This is useful when you wish
// We can optionally give the node a name. for all nodes to load a common CorDapp but for a subset of nodes to load CorDapps specific to their role in the network.
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<String> = 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 Running the network
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,35 @@
package net.corda.docs.java;
// DOCSTART 1
import net.corda.core.identity.CordaX500Name;
import net.corda.testing.node.MockNetwork;
import net.corda.testing.node.MockNetworkParameters;
import net.corda.testing.node.StartedMockNode;
import org.junit.After;
import org.junit.Before;
import static java.util.Collections.singletonList;
import static net.corda.testing.node.TestCordapp.findCordapp;
public class MockNetworkTestsTutorial {
private final MockNetwork mockNet = new MockNetwork(new MockNetworkParameters(singletonList(findCordapp("com.mycordapp.package"))));
@After
public void cleanUp() {
mockNet.stopNodes();
}
// DOCEND 1
// DOCSTART 2
private StartedMockNode nodeA;
private StartedMockNode nodeB;
@Before
public void setUp() {
nodeA = mockNet.createNode();
// We can optionally give the node a name.
nodeB = mockNet.createNode(new CordaX500Name("Bank B", "London", "GB"));
}
// DOCEND 2
}

View File

@ -0,0 +1,33 @@
package net.corda.docs.kotlin
// DOCSTART 1
import net.corda.core.identity.CordaX500Name
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
import net.corda.testing.node.TestCordapp.Companion.findCordapp
import org.junit.After
import org.junit.Before
class MockNetworkTestsTutorial {
private val mockNet = MockNetwork(MockNetworkParameters(listOf(findCordapp("com.mycordapp.package"))))
@After
fun cleanUp() {
mockNet.stopNodes()
}
// DOCEND 1
// DOCSTART 2
private lateinit var nodeA: StartedMockNode
private lateinit var nodeB: StartedMockNode
@Before
fun setUp() {
nodeA = mockNet.createNode()
// We can optionally give the node a name.
nodeB = mockNet.createNode(CordaX500Name("Bank B", "London", "GB"))
}
// DOCEND 2
}

View File

@ -269,19 +269,10 @@ Here's an example of it in action from ``FixingFlow.Fixer``.
Testing Testing
------- -------
The ``MockNetwork`` allows the creation of ``MockNode`` instances, which are simplified nodes which can be used for The ``MockNetwork`` allows the creation of ``MockNode`` instances, which are simplified nodes which can be used for testing (see :doc:`api-testing`).
testing (see :doc:`api-testing`). When creating the ``MockNetwork`` you supply a list of packages to scan for CorDapps. When creating the ``MockNetwork`` you supply a list of ``TestCordapp`` objects which point to CorDapps on
Make sure the packages you provide include your oracle service, and it automatically be installed in the test nodes. the classpath. These CorDapps will be installed on each node on the network. Make sure the packages you provide reference to the CorDapp
Then you can create an oracle node on the ``MockNetwork`` and insert any initialisation logic you want to use. In this containing your oracle service.
case, our ``Oracle`` service is in the ``net.corda.irs.api`` package, so the following test setup will install
the service in each node. Then an oracle node with an oracle service which is initialised with some data is created on
the mock network:
.. literalinclude:: ../../samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
:dedent: 4
You can then write tests on your mock network to verify the nodes interact with your Oracle correctly. You can then write tests on your mock network to verify the nodes interact with your Oracle correctly.

View File

@ -10,6 +10,7 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import net.corda.testing.node.StartedMockNode import net.corda.testing.node.StartedMockNode
import org.junit.After import org.junit.After
@ -26,7 +27,7 @@ class FlowRegistrationTest {
@Before @Before
fun setup() { fun setup() {
// no cordapps scanned so it can be tested in isolation // no cordapps scanned so it can be tested in isolation
mockNetwork = MockNetwork() mockNetwork = MockNetwork(MockNetworkParameters())
initiator = mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("initiator", "Reading", "GB"))) initiator = mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("initiator", "Reading", "GB")))
responder = mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("responder", "Reading", "GB"))) responder = mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("responder", "Reading", "GB")))
mockNetwork.runNetwork() mockNetwork.runNetwork()

View File

@ -68,7 +68,7 @@ class OracleNodeTearOffTests {
mockNet.stopNodes() mockNet.stopNodes()
} }
// DOCSTART 2 // DOCSTART 1
@Test @Test
fun `verify that the oracle signs the transaction if the interest rate within allowed limit`() { fun `verify that the oracle signs the transaction if the interest rate within allowed limit`() {
// Create a partial transaction // Create a partial transaction
@ -93,7 +93,7 @@ class OracleNodeTearOffTests {
// Check that the transaction has been signed by the oracle // Check that the transaction has been signed by the oracle
assertContains(fix.signers, oracle.owningKey) assertContains(fix.signers, oracle.owningKey)
} }
// DOCEND 2 // DOCEND 1
@Test @Test
fun `verify that the oracle rejects the transaction if the interest rate is outside the allowed limit`() { fun `verify that the oracle rejects the transaction if the interest rate is outside the allowed limit`() {

View File

@ -10,7 +10,7 @@ class MockNetworkIntegrationTests {
companion object { companion object {
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
MockNetwork().run { MockNetwork(MockNetworkParameters()).run {
repeat(2) { createNode() } repeat(2) { createNode() }
runNetwork() runNetwork()
stopNodes() stopNodes()

View File

@ -248,7 +248,9 @@ data class DriverParameters(
val notaryCustomOverrides: Map<String, Any?> = emptyMap(), val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
val inMemoryDB: Boolean = true, val inMemoryDB: Boolean = true,
val cordappsForAllNodes: Collection<TestCordapp>? = null val cordappsForAllNodes: Collection<TestCordapp>? = null
) { ) {
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes)
constructor( constructor(
isDebug: Boolean = false, isDebug: Boolean = false,
driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(), driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(),
@ -313,38 +315,6 @@ data class DriverParameters(
cordappsForAllNodes = null cordappsForAllNodes = null
) )
constructor(
isDebug: Boolean,
driverDirectory: Path,
portAllocation: PortAllocation,
debugPortAllocation: PortAllocation,
systemProperties: Map<String, String>,
useTestClock: Boolean,
startNodesInProcess: Boolean,
waitForAllNodesToFinish: Boolean,
notarySpecs: List<NotarySpec>,
extraCordappPackagesToScan: List<String>,
jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters,
cordappsForAllNodes: Collection<TestCordapp>? = null
) : this(
isDebug,
driverDirectory,
portAllocation,
debugPortAllocation,
systemProperties,
useTestClock,
startNodesInProcess,
waitForAllNodesToFinish,
notarySpecs,
extraCordappPackagesToScan,
jmxPolicy,
networkParameters,
emptyMap(),
true,
cordappsForAllNodes
)
constructor( constructor(
isDebug: Boolean, isDebug: Boolean,
driverDirectory: Path, driverDirectory: Path,
@ -377,39 +347,6 @@ data class DriverParameters(
cordappsForAllNodes = null cordappsForAllNodes = null
) )
constructor(
isDebug: Boolean,
driverDirectory: Path,
portAllocation: PortAllocation,
debugPortAllocation: PortAllocation,
systemProperties: Map<String, String>,
useTestClock: Boolean,
startNodesInProcess: Boolean,
waitForAllNodesToFinish: Boolean,
notarySpecs: List<NotarySpec>,
extraCordappPackagesToScan: List<String>,
jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters,
inMemoryDB: Boolean,
cordappsForAllNodes: Set<TestCordapp>? = null
) : this(
isDebug,
driverDirectory,
portAllocation,
debugPortAllocation,
systemProperties,
useTestClock,
startNodesInProcess,
waitForAllNodesToFinish,
notarySpecs,
extraCordappPackagesToScan,
jmxPolicy,
networkParameters,
emptyMap(),
inMemoryDB,
cordappsForAllNodes
)
fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug) fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug)
fun withDriverDirectory(driverDirectory: Path): DriverParameters = copy(driverDirectory = driverDirectory) fun withDriverDirectory(driverDirectory: Path): DriverParameters = copy(driverDirectory = driverDirectory)
fun withPortAllocation(portAllocation: PortAllocation): DriverParameters = copy(portAllocation = portAllocation) fun withPortAllocation(portAllocation: PortAllocation): DriverParameters = copy(portAllocation = portAllocation)
@ -419,6 +356,8 @@ data class DriverParameters(
fun withStartNodesInProcess(startNodesInProcess: Boolean): DriverParameters = copy(startNodesInProcess = startNodesInProcess) fun withStartNodesInProcess(startNodesInProcess: Boolean): DriverParameters = copy(startNodesInProcess = startNodesInProcess)
fun withWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean): DriverParameters = copy(waitForAllNodesToFinish = waitForAllNodesToFinish) fun withWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean): DriverParameters = copy(waitForAllNodesToFinish = waitForAllNodesToFinish)
fun withNotarySpecs(notarySpecs: List<NotarySpec>): DriverParameters = copy(notarySpecs = notarySpecs) fun withNotarySpecs(notarySpecs: List<NotarySpec>): DriverParameters = copy(notarySpecs = notarySpecs)
@Deprecated("extraCordappPackagesToScan does not preserve the original CorDapp's versioning and metadata, which may lead to " +
"misleading results in tests. Use withCordappsForAllNodes instead.")
fun withExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>): DriverParameters = copy(extraCordappPackagesToScan = extraCordappPackagesToScan) fun withExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>): DriverParameters = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
fun withJmxPolicy(jmxPolicy: JmxPolicy): DriverParameters = copy(jmxPolicy = jmxPolicy) fun withJmxPolicy(jmxPolicy: JmxPolicy): DriverParameters = copy(jmxPolicy = jmxPolicy)
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters) fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)

View File

@ -61,8 +61,7 @@ data class MockNodeParameters(
} }
/** /**
* Immutable builder for configuring a [MockNetwork]. Kotlin users can also use named parameters to the constructor * Immutable builder for configuring a [MockNetwork].
* of [MockNetwork], which is more convenient.
* *
* @property networkSendManuallyPumped If false then messages will not be routed from sender to receiver until you use * @property networkSendManuallyPumped If false 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 * the [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can examine the
@ -95,6 +94,8 @@ data class MockNetworkParameters(
networkParameters: NetworkParameters networkParameters: NetworkParameters
) : this(networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, emptyList()) ) : this(networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, emptyList())
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(threadPerNode = false, cordappsForAllNodes = cordappsForAllNodes)
fun withNetworkParameters(networkParameters: NetworkParameters): MockNetworkParameters = copy(networkParameters = networkParameters) fun withNetworkParameters(networkParameters: NetworkParameters): MockNetworkParameters = copy(networkParameters = networkParameters)
fun withNetworkSendManuallyPumped(networkSendManuallyPumped: Boolean): MockNetworkParameters = copy(networkSendManuallyPumped = networkSendManuallyPumped) fun withNetworkSendManuallyPumped(networkSendManuallyPumped: Boolean): MockNetworkParameters = copy(networkSendManuallyPumped = networkSendManuallyPumped)
fun withThreadPerNode(threadPerNode: Boolean): MockNetworkParameters = copy(threadPerNode = threadPerNode) fun withThreadPerNode(threadPerNode: Boolean): MockNetworkParameters = copy(threadPerNode = threadPerNode)
@ -108,7 +109,8 @@ data class MockNetworkParameters(
threadPerNode: Boolean, threadPerNode: Boolean,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy, servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy,
notarySpecs: List<MockNetworkNotarySpec>, notarySpecs: List<MockNetworkNotarySpec>,
networkParameters: NetworkParameters): MockNetworkParameters { networkParameters: NetworkParameters
): MockNetworkParameters {
return MockNetworkParameters(networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, emptyList()) return MockNetworkParameters(networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, emptyList())
} }
} }
@ -243,9 +245,9 @@ class StartedMockNode private constructor(private val node: TestStartedNode) {
* By default a single notary node is automatically started, which forms part of the network parameters for all the nodes. * By default a single notary node is automatically started, which forms part of the network parameters for all the nodes.
* This node is available by calling [defaultNotaryNode]. * This node is available by calling [defaultNotaryNode].
* *
* @property defaultParameters The default parameters for the network. If any of the remaining constructor parameters are specified then
* their values are taken instead of the corresponding value in [defaultParameters].
* @property cordappPackages A [List] of cordapp packages to scan for any cordapp code, e.g. contract verification code, flows and services. * @property cordappPackages A [List] of cordapp packages to scan for any cordapp code, e.g. contract verification code, flows and services.
* @property defaultParameters A [MockNetworkParameters] object which contains the same parameters as the constructor, provided
* as a convenience for Java users.
* @property networkSendManuallyPumped If false then messages will not be routed from sender to receiver until you use * @property networkSendManuallyPumped If false 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 * 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 * state of the mock network before and after a message is sent, without races and without the receiving node immediately
@ -279,8 +281,7 @@ open class MockNetwork(
cordappPackages, defaultParameters = parameters cordappPackages, defaultParameters = parameters
) )
@JvmOverloads constructor(parameters: MockNetworkParameters) : this(emptyList(), defaultParameters = parameters)
constructor(parameters: MockNetworkParameters = MockNetworkParameters()) : this(emptyList(), parameters)
private val internalMockNetwork = InternalMockNetwork( private val internalMockNetwork = InternalMockNetwork(
cordappPackages, cordappPackages,
@ -317,7 +318,7 @@ open class MockNetwork(
fun createPartyNode(legalName: CordaX500Name? = null): StartedMockNode = StartedMockNode.create(internalMockNetwork.createPartyNode(legalName)) fun createPartyNode(legalName: CordaX500Name? = null): StartedMockNode = StartedMockNode.create(internalMockNetwork.createPartyNode(legalName))
/** Create a started node with the given parameters. **/ /** Create a started node with the given parameters. **/
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode { fun createNode(parameters: MockNodeParameters): StartedMockNode {
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters))) return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
} }

View File

@ -6,7 +6,7 @@ import net.corda.testing.driver.NodeParameters
import net.corda.testing.node.internal.TestCordappImpl import net.corda.testing.node.internal.TestCordappImpl
/** /**
* Encapsulates a CorDapp that exists on the current classpath, which can be pulled in for testing. Use [TestCordapp.Factory.findCordapp] * Encapsulates a CorDapp that exists on the current classpath, which can be pulled in for testing. Use [TestCordapp.findCordapp]
* to locate an existing CorDapp. * to locate an existing CorDapp.
* *
* @see DriverParameters.cordappsForAllNodes * @see DriverParameters.cordappsForAllNodes
@ -15,27 +15,25 @@ import net.corda.testing.node.internal.TestCordappImpl
* @see MockNodeParameters.additionalCordapps * @see MockNodeParameters.additionalCordapps
*/ */
@DoNotImplement @DoNotImplement
interface TestCordapp { abstract class TestCordapp {
/** The package used to find the CorDapp. This may not be the root package of the CorDapp. */ /** The package used to find the CorDapp. This may not be the root package of the CorDapp. */
val scanPackage: String abstract val scanPackage: String
/** Returns the config for on this CorDapp, defaults to empty if not specified. */ /** Returns the config for on this CorDapp, defaults to empty if not specified. */
val config: Map<String, Any> abstract val config: Map<String, Any>
/** Returns a copy of this [TestCordapp] but with the specified CorDapp config. */ /** Returns a copy of this [TestCordapp] but with the specified CorDapp config. */
fun withConfig(config: Map<String, Any>): TestCordapp abstract fun withConfig(config: Map<String, Any>): TestCordapp
class Factory { companion object {
companion object { /**
/** * Scans the current classpath to find the CorDapp that contains the given package. All the CorDapp's metdata present in its
* Scans the current classpath to find the CorDapp that contains the given package. All the CorDapp's metdata present in its * MANIFEST are inherited. If more than one location containing the package is found then an exception is thrown. An exception
* MANIFEST are inherited. If more than one location containing the package is found then an exception is thrown. An exception * is also thrown if no CorDapp is found.
* is also thrown if no CorDapp is found. *
* * @param scanPackage The package name used to find the CorDapp. This does not need to be the root package of the CorDapp.
* @param scanPackage The package name used to find the CorDapp. This does not need to be the root package. */
*/ @JvmStatic
@JvmStatic fun findCordapp(scanPackage: String): TestCordapp = TestCordappImpl(scanPackage = scanPackage, config = emptyMap())
fun findCordapp(scanPackage: String): TestCordapp = TestCordappImpl(scanPackage = scanPackage, config = emptyMap())
}
} }
} }

View File

@ -32,7 +32,7 @@ data class CustomCordapp(
val classes: Set<Class<*>> = emptySet(), val classes: Set<Class<*>> = emptySet(),
val signingInfo: SigningInfo? = null, val signingInfo: SigningInfo? = null,
override val config: Map<String, Any> = emptyMap() override val config: Map<String, Any> = emptyMap()
) : TestCordappInternal { ) : TestCordappInternal() {
init { init {
require(packages.isNotEmpty() || classes.isNotEmpty()) { "At least one package or class must be specified" } require(packages.isNotEmpty() || classes.isNotEmpty()) { "At least one package or class must be specified" }
} }

View File

@ -13,12 +13,12 @@ import kotlin.streams.toList
/** /**
* Implementation of the public [TestCordapp] API. * Implementation of the public [TestCordapp] API.
* *
* As described in [TestCordapp.Factory.findCordapp], this represents a single CorDapp jar on the current classpath. The [scanPackage] may * As described in [TestCordapp.findCordapp], this represents a single CorDapp jar on the current classpath. The [scanPackage] may
* be for an external dependency to the project that's using this API, in which case that dependency jar is referenced as is. On the other hand, * be for an external dependency to the project that's using this API, in which case that dependency jar is referenced as is. On the other hand,
* the [scanPackage] may reference a gradle CorDapp project on the local system. In this scenerio the project's "jar" task is executed to * the [scanPackage] may reference a gradle CorDapp project on the local system. In this scenerio the project's "jar" task is executed to
* build the CorDapp jar. This allows us to inherit the CorDapp's MANIFEST information without having to do any extra processing. * build the CorDapp jar. This allows us to inherit the CorDapp's MANIFEST information without having to do any extra processing.
*/ */
data class TestCordappImpl(override val scanPackage: String, override val config: Map<String, Any>) : TestCordappInternal { data class TestCordappImpl(override val scanPackage: String, override val config: Map<String, Any>) : TestCordappInternal() {
override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config) override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config)
override fun withOnlyJarContents(): TestCordappImpl = copy(config = emptyMap()) override fun withOnlyJarContents(): TestCordappImpl = copy(config = emptyMap())
@ -27,9 +27,11 @@ data class TestCordappImpl(override val scanPackage: String, override val config
get() { get() {
val jars = TestCordappImpl.findJars(scanPackage) val jars = TestCordappImpl.findJars(scanPackage)
when (jars.size) { when (jars.size) {
0 -> throw IllegalArgumentException("Package $scanPackage does not exist") 0 -> throw IllegalArgumentException("There are no CorDapps containing the package $scanPackage on the classpath. Make sure " +
"the package name is correct and that the CorDapp is added as a gradle dependency.")
1 -> return jars.first() 1 -> return jars.first()
else -> throw IllegalArgumentException("More than one jar found containing package $scanPackage: $jars") else -> throw IllegalArgumentException("There is more than one CorDapp containing the package $scanPackage on the classpath " +
"$jars. Specify a package name which is unique to the CorDapp.")
} }
} }

View File

@ -14,11 +14,11 @@ import java.nio.file.StandardCopyOption.REPLACE_EXISTING
* *
* @property jarFile The jar file this CorDapp represents. Different CorDapps may point to the same file. * @property jarFile The jar file this CorDapp represents. Different CorDapps may point to the same file.
*/ */
interface TestCordappInternal : TestCordapp { abstract class TestCordappInternal : TestCordapp() {
val jarFile: Path abstract val jarFile: Path
/** Return a copy of this TestCordappInternal but without any metadata, such as configs and signing information. */ /** Return a copy of this TestCordappInternal but without any metadata, such as configs and signing information. */
fun withOnlyJarContents(): TestCordappInternal abstract fun withOnlyJarContents(): TestCordappInternal
companion object { companion object {
fun installCordapps(baseDirectory: Path, fun installCordapps(baseDirectory: Path,

View File

@ -44,9 +44,9 @@ fun cordappForClasses(vararg classes: Class<*>): CustomCordapp = CustomCordapp(p
/** /**
* Find the single CorDapp jar on the current classpath which contains the given package. This is a convenience method for * Find the single CorDapp jar on the current classpath which contains the given package. This is a convenience method for
* [TestCordapp.Factory.findCordapp] but returns the internal [TestCordappImpl]. * [TestCordapp.findCordapp] but returns the internal [TestCordappImpl].
*/ */
fun findCordapp(scanPackage: String): TestCordappImpl = TestCordapp.Factory.findCordapp(scanPackage) as TestCordappImpl fun findCordapp(scanPackage: String): TestCordappImpl = TestCordapp.findCordapp(scanPackage) as TestCordappImpl
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? { fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
val stackTrace = Throwable().stackTrace val stackTrace = Throwable().stackTrace

View File

@ -16,7 +16,7 @@ class MockNetworkTest {
@Before @Before
fun setup() { fun setup() {
mockNetwork = MockNetwork() mockNetwork = MockNetwork(MockNetworkParameters())
} }
@After @After