corda/docs/build/html/tutorial-integration-testing.html
2017-01-06 17:38:23 +00:00

424 lines
26 KiB
HTML

<!-- If you edit this, then please make the same changes to layout_for_doc_website.html, as that is used for the web
doc site generation which we put analytics tracking on to identify any potential problem pages -->
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Integration testing &mdash; R3 Corda latest documentation</title>
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
<link rel="top" title="R3 Corda latest documentation" href="index.html"/>
<link rel="next" title="Client RPC API tutorial" href="tutorial-clientrpc-api.html"/>
<link rel="prev" title="Writing a contract test" href="tutorial-test-dsl.html"/>
<script src="_static/js/modernizr.min.js"></script>
</head>
<body class="wy-body-for-nav" role="document">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search">
<a href="index.html" class="icon icon-home"> R3 Corda
</a>
<div class="version">
latest
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<br>
<a href="api/index.html">API reference</a>
<br>
<a href="https://discourse.corda.net">Discourse Forums</a>
<br>
<a href="http://slack.corda.net">Slack</a>
<br>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<p class="caption"><span class="caption-text">Getting started</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What&#8217;s included?</a></li>
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
<li class="toctree-l1"><a class="reference internal" href="getting-set-up-fault-finding.html">Troubleshooting</a></li>
<li class="toctree-l1"><a class="reference internal" href="running-the-demos.html">Running the demos</a></li>
<li class="toctree-l1"><a class="reference internal" href="CLI-vs-IDE.html">CLI vs IDE</a></li>
</ul>
<p class="caption"><span class="caption-text">Key concepts</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
<li class="toctree-l1"><a class="reference internal" href="transaction-data-types.html">Data types</a></li>
<li class="toctree-l1"><a class="reference internal" href="merkle-trees.html">Transaction tear-offs</a></li>
<li class="toctree-l1"><a class="reference internal" href="consensus.html">Consensus model</a></li>
<li class="toctree-l1"><a class="reference internal" href="clauses.html">Clauses</a></li>
</ul>
<p class="caption"><span class="caption-text">CorDapps</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="creating-a-cordapp.html">CorDapp basics</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-cordapp.html">The CorDapp template</a></li>
</ul>
<p class="caption"><span class="caption-text">The Corda node</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="clientrpc.html">Client RPC</a></li>
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
<li class="toctree-l1"><a class="reference internal" href="persistence.html">Persistence</a></li>
<li class="toctree-l1"><a class="reference internal" href="node-administration.html">Node administration</a></li>
<li class="toctree-l1"><a class="reference internal" href="corda-configuration-file.html">Node configuration</a></li>
<li class="toctree-l1"><a class="reference internal" href="corda-plugins.html">The Corda plugin framework</a></li>
<li class="toctree-l1"><a class="reference internal" href="node-services.html">Brief introduction to the node services</a></li>
<li class="toctree-l1"><a class="reference internal" href="node-explorer.html">Node Explorer</a></li>
<li class="toctree-l1"><a class="reference internal" href="permissioning.html">Network permissioning</a></li>
</ul>
<p class="caption"><span class="caption-text">Tutorials</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="tutorial-contract.html">Writing a contract</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-contract-clauses.html">Writing a contract using clauses</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-test-dsl.html">Writing a contract test</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Integration testing</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-clientrpc-api.html">Client RPC API tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-building-transactions.html">Building transactions</a></li>
<li class="toctree-l1"><a class="reference internal" href="flow-state-machines.html">Writing flows</a></li>
<li class="toctree-l1"><a class="reference internal" href="flow-testing.html">Writing flow tests</a></li>
<li class="toctree-l1"><a class="reference internal" href="running-a-notary.html">Running a notary service</a></li>
<li class="toctree-l1"><a class="reference internal" href="using-a-notary.html">Using a notary service</a></li>
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-attachments.html">Using attachments</a></li>
<li class="toctree-l1"><a class="reference internal" href="event-scheduling.html">Event scheduling</a></li>
</ul>
<p class="caption"><span class="caption-text">Other</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="network-simulator.html">Network Simulator</a></li>
</ul>
<p class="caption"><span class="caption-text">Component library</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="contract-catalogue.html">Contract catalogue</a></li>
<li class="toctree-l1"><a class="reference internal" href="contract-irs.html">Interest rate swaps</a></li>
</ul>
<p class="caption"><span class="caption-text">Appendix</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="loadtesting.html">Load testing</a></li>
<li class="toctree-l1"><a class="reference internal" href="setting-up-a-corda-network.html">What is a corda network?</a></li>
<li class="toctree-l1"><a class="reference internal" href="secure-coding-guidelines.html">Secure coding guidelines</a></li>
<li class="toctree-l1"><a class="reference internal" href="release-process.html">Release process</a></li>
<li class="toctree-l1"><a class="reference internal" href="release-notes.html">Release notes</a></li>
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
<li class="toctree-l1"><a class="reference internal" href="building-the-docs.html">Building the documentation</a></li>
<li class="toctree-l1"><a class="reference internal" href="publishing-corda.html">Publishing Corda</a></li>
<li class="toctree-l1"><a class="reference internal" href="azure-vm.html">Working with the Corda Demo on Azure Marketplace</a></li>
</ul>
<p class="caption"><span class="caption-text">Glossary</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="glossary.html">Glossary</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">R3 Corda</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html">Docs</a> &raquo;</li>
<li>Integration testing</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/tutorial-integration-testing.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="integration-testing">
<h1>Integration testing<a class="headerlink" href="#integration-testing" title="Permalink to this headline"></a></h1>
<p>Integration testing involves bringing up nodes locally and testing
invariants about them by starting flows and inspecting their state.</p>
<p>In this tutorial we will bring up three nodes Alice, Bob and a
Notary. Alice will issue Cash to Bob, then Bob will send this Cash
back to Alice. We will see how to test some simple deterministic and
nondeterministic invariants in the meantime.</p>
<p>(Note that this example where Alice is self-issuing Cash is purely for
demonstration purposes, in reality Cash would be issued by a bank and
subsequently passed around.)</p>
<p>In order to spawn nodes we will use the Driver DSL. This DSL allows
one to start up node processes from code. It manages a network map
service and safe shutting down of nodes in the background.</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> driver {
val testUser = User(&quot;testUser&quot;, &quot;testPassword&quot;, permissions = setOf(startFlowPermission&lt;CashFlow&gt;()))
val (alice, bob, notary) = Futures.allAsList(
startNode(&quot;Alice&quot;, rpcUsers = listOf(testUser)),
startNode(&quot;Bob&quot;, rpcUsers = listOf(testUser)),
startNode(&quot;Notary&quot;, advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
).getOrThrow()
</pre></div>
</div>
<p>The above code creates a <code class="docutils literal"><span class="pre">User</span></code> permissioned to start the
<code class="docutils literal"><span class="pre">CashFlow</span></code> protocol. It then starts up Alice and Bob with this user,
allowing us to later connect to the nodes.</p>
<p>Then the notary is started up. Note that we need to add
<code class="docutils literal"><span class="pre">ValidatingNotaryService</span></code> as an advertised service in order for this
node to serve notary functionality. This is also where flows added in
plugins should be specified. Note also that we won&#8217;t connect to the
notary directly, so there&#8217;s no need to pass in the test <code class="docutils literal"><span class="pre">User</span></code>.</p>
<p>The <code class="docutils literal"><span class="pre">startNode</span></code> function returns a future that completes once the
node is fully started. This allows starting of the nodes to be
parallel. We wait on these futures as we need the information
returned; their respective <code class="docutils literal"><span class="pre">NodeHandles</span></code> s.</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> <span class="k">val</span> <span class="py">aliceClient</span> <span class="p">=</span> <span class="n">alice</span><span class="p">.</span><span class="n">rpcClientToNode</span><span class="p">()</span>
<span class="n">aliceClient</span><span class="p">.</span><span class="n">start</span><span class="p">(</span><span class="s">&quot;testUser&quot;</span><span class="p">,</span> <span class="s">&quot;testPassword&quot;</span><span class="p">)</span>
<span class="k">val</span> <span class="py">aliceProxy</span> <span class="p">=</span> <span class="n">aliceClient</span><span class="p">.</span><span class="n">proxy</span><span class="p">()</span>
<span class="k">val</span> <span class="py">bobClient</span> <span class="p">=</span> <span class="n">bob</span><span class="p">.</span><span class="n">rpcClientToNode</span><span class="p">()</span>
<span class="n">bobClient</span><span class="p">.</span><span class="n">start</span><span class="p">(</span><span class="s">&quot;testUser&quot;</span><span class="p">,</span> <span class="s">&quot;testPassword&quot;</span><span class="p">)</span>
<span class="k">val</span> <span class="py">bobProxy</span> <span class="p">=</span> <span class="n">bobClient</span><span class="p">.</span><span class="n">proxy</span><span class="p">()</span>
</pre></div>
</div>
<p>Next we connect to Alice and Bob respectively from the test process
using the test user we created. Then we establish RPC links that allow
us to start flows and query state.</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> <span class="k">val</span> <span class="py">bobVaultUpdates</span> <span class="p">=</span> <span class="n">bobProxy</span><span class="p">.</span><span class="n">vaultAndUpdates</span><span class="p">().</span><span class="n">second</span>
<span class="k">val</span> <span class="py">aliceVaultUpdates</span> <span class="p">=</span> <span class="n">aliceProxy</span><span class="p">.</span><span class="n">vaultAndUpdates</span><span class="p">().</span><span class="n">second</span>
</pre></div>
</div>
<p>We will be interested in changes to Alice&#8217;s and Bob&#8217;s vault, so we
query a stream of vault updates from each.</p>
<p>Now that we&#8217;re all set up we can finally get some Cash action going!</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> <span class="k">val</span> <span class="py">issueRef</span> <span class="p">=</span> <span class="n">OpaqueBytes</span><span class="p">.</span><span class="n">of</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="m">1</span> <span class="p">..</span> <span class="m">10</span><span class="p">)</span> <span class="p">{</span>
<span class="n">thread</span> <span class="p">{</span>
<span class="n">aliceProxy</span><span class="p">.</span><span class="n">startFlow</span><span class="p">(</span><span class="o">::</span><span class="n">CashFlow</span><span class="p">,</span> <span class="n">CashCommand</span><span class="p">.</span><span class="n">IssueCash</span><span class="p">(</span>
<span class="n">amount</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="n">DOLLARS</span><span class="p">,</span>
<span class="n">issueRef</span> <span class="p">=</span> <span class="n">issueRef</span><span class="p">,</span>
<span class="n">recipient</span> <span class="p">=</span> <span class="n">bob</span><span class="p">.</span><span class="n">nodeInfo</span><span class="p">.</span><span class="n">legalIdentity</span><span class="p">,</span>
<span class="n">notary</span> <span class="p">=</span> <span class="n">notary</span><span class="p">.</span><span class="n">nodeInfo</span><span class="p">.</span><span class="n">notaryIdentity</span>
<span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">bobVaultUpdates</span><span class="p">.</span><span class="n">expectEvents</span> <span class="p">{</span>
<span class="n">parallel</span><span class="p">(</span>
<span class="p">(</span><span class="m">1</span> <span class="p">..</span> <span class="m">10</span><span class="p">).</span><span class="n">map</span> <span class="p">{</span> <span class="n">i</span> <span class="p">-&gt;</span>
<span class="n">expect</span><span class="p">(</span>
<span class="n">match</span> <span class="p">=</span> <span class="p">{</span> <span class="n">update</span><span class="p">:</span> <span class="n">Vault</span><span class="p">.</span><span class="n">Update</span> <span class="p">-&gt;</span>
<span class="p">(</span><span class="n">update</span><span class="p">.</span><span class="n">produced</span><span class="p">.</span><span class="n">first</span><span class="p">().</span><span class="n">state</span><span class="p">.</span><span class="k">data</span> <span class="k">as</span> <span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">).</span><span class="n">amount</span><span class="p">.</span><span class="n">quantity</span> <span class="p">==</span> <span class="n">i</span> <span class="p">*</span> <span class="m">100L</span>
<span class="p">}</span>
<span class="p">)</span> <span class="p">{</span> <span class="n">update</span> <span class="p">-&gt;</span>
<span class="n">println</span><span class="p">(</span><span class="s">&quot;Bob vault update of $update&quot;</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="p">}</span>
</pre></div>
</div>
<p>The first loop creates 10 threads, each starting a <code class="docutils literal"><span class="pre">CashFlow</span></code> flow
on the Alice node. We specify that we want to issue <code class="docutils literal"><span class="pre">i</span></code> dollars to
Bob, using the Notary as the notary responsible for notarising the
created states. Note that no notarisation will occur yet as we&#8217;re not
spending any states, only entering new ones to the ledger.</p>
<p>We started the flows from different threads for the sake of the
tutorial, to demonstrate how to test non-determinism, which is what
the <code class="docutils literal"><span class="pre">expectEvents</span></code> block does.</p>
<p>The Expect DSL allows ordering constraints to be checked on a stream
of events. The above code specifies that we are expecting 10 updates
to be emitted on the <code class="docutils literal"><span class="pre">bobVaultUpdates</span></code> stream in unspecified order
(this is what the <code class="docutils literal"><span class="pre">parallel</span></code> construct does). We specify a
(otherwise optional) <code class="docutils literal"><span class="pre">match</span></code> predicate to identify specific updates
we are interested in, which we then print.</p>
<p>If we run the code written so far we should see 4 nodes starting up
(Alice,Bob,Notary + implicit Network Map service), then 10 logs of Bob
receiving 1,2,...10 dollars from Alice in some unspecified order.</p>
<p>Next we want Bob to send this Cash back to Alice.</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="k">in</span> <span class="m">1</span> <span class="p">..</span> <span class="m">10</span><span class="p">)</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">flowHandle</span> <span class="p">=</span> <span class="n">bobProxy</span><span class="p">.</span><span class="n">startFlow</span><span class="p">(</span><span class="o">::</span><span class="n">CashFlow</span><span class="p">,</span> <span class="n">CashCommand</span><span class="p">.</span><span class="n">PayCash</span><span class="p">(</span>
<span class="n">amount</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="n">DOLLARS</span><span class="p">.</span><span class="n">issuedBy</span><span class="p">(</span><span class="n">alice</span><span class="p">.</span><span class="n">nodeInfo</span><span class="p">.</span><span class="n">legalIdentity</span><span class="p">.</span><span class="n">ref</span><span class="p">(</span><span class="n">issueRef</span><span class="p">)),</span>
<span class="n">recipient</span> <span class="p">=</span> <span class="n">alice</span><span class="p">.</span><span class="n">nodeInfo</span><span class="p">.</span><span class="n">legalIdentity</span>
<span class="p">))</span>
<span class="n">assert</span><span class="p">(</span><span class="n">flowHandle</span><span class="p">.</span><span class="n">returnValue</span><span class="p">.</span><span class="n">toBlocking</span><span class="p">().</span><span class="n">first</span><span class="p">()</span> <span class="k">is</span> <span class="n">CashFlowResult</span><span class="p">.</span><span class="n">Success</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">aliceVaultUpdates</span><span class="p">.</span><span class="n">expectEvents</span> <span class="p">{</span>
<span class="n">sequence</span><span class="p">(</span>
<span class="p">(</span><span class="m">1</span> <span class="p">..</span> <span class="m">10</span><span class="p">).</span><span class="n">map</span> <span class="p">{</span> <span class="n">i</span> <span class="p">-&gt;</span>
<span class="n">expect</span> <span class="p">{</span> <span class="n">update</span><span class="p">:</span> <span class="n">Vault</span><span class="p">.</span><span class="n">Update</span> <span class="p">-&gt;</span>
<span class="n">println</span><span class="p">(</span><span class="s">&quot;Alice got vault update of $update&quot;</span><span class="p">)</span>
<span class="n">assertEquals</span><span class="p">((</span><span class="n">update</span><span class="p">.</span><span class="n">produced</span><span class="p">.</span><span class="n">first</span><span class="p">().</span><span class="n">state</span><span class="p">.</span><span class="k">data</span> <span class="k">as</span> <span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">).</span><span class="n">amount</span><span class="p">.</span><span class="n">quantity</span><span class="p">,</span> <span class="n">i</span> <span class="p">*</span> <span class="m">100L</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>This time we&#8217;ll do it sequentially. We make Bob pay 1,2,..10 dollars
to Alice in order. We make sure that a the <code class="docutils literal"><span class="pre">CashFlow</span></code> has finished
by waiting on <code class="docutils literal"><span class="pre">startFlow</span></code> &#8216;s <code class="docutils literal"><span class="pre">returnValue</span></code>.</p>
<p>Then we use the Expect DSL again, this time using <code class="docutils literal"><span class="pre">sequence</span></code> to test
for the updates arriving in the order we expect them to.</p>
<p>Note that <code class="docutils literal"><span class="pre">parallel</span></code> and <code class="docutils literal"><span class="pre">sequence</span></code> may be nested into each other
arbitrarily to test more complex scenarios.</p>
<p>That&#8217;s it! We saw how to start up several corda nodes locally, how to
connect to them, and how to test some simple invariants about
<code class="docutils literal"><span class="pre">CashFlow</span></code>.</p>
<p>To run the complete test you can open
<code class="docutils literal"><span class="pre">example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt</span></code>
from IntelliJ and run the test, or alternatively use gradle:</p>
<div class="highlight-bash"><div class="highlight"><pre><span></span><span class="c1"># Run example-code integration tests</span>
./gradlew docs/source/example-code:integrationTest -i
</pre></div>
</div>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="tutorial-clientrpc-api.html" class="btn btn-neutral float-right" title="Client RPC API tutorial" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="tutorial-test-dsl.html" class="btn btn-neutral" title="Writing a contract test" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2016, R3 Limited.
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'./',
VERSION:'latest',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/js/theme.js"></script>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.StickyNav.enable();
});
</script>
</body>
</html>