corda/docs/build/html/flow-testing.html

379 lines
22 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>Writing flow tests &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="Running a notary service" href="running-a-notary.html"/>
<link rel="prev" title="Writing flows" href="flow-state-machines.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>
API reference: <a href="api/kotlin/corda/index.html">Kotlin</a>/ <a href="api/javadoc/index.html">JavaDoc</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"><a class="reference internal" href="tutorial-integration-testing.html">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 current"><a class="current reference internal" href="#">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>Writing flow tests</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/flow-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">
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script><div class="section" id="writing-flow-tests">
<h1>Writing flow tests<a class="headerlink" href="#writing-flow-tests" title="Permalink to this headline"></a></h1>
<p>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.</p>
<p>A good example to examine for learning how to unit test flows is the <code class="docutils literal"><span class="pre">ResolveTransactionsFlow</span></code> tests. This
flow takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start
with this basic skeleton:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ResolveTransactionsFlowTest</span> <span class="p">{</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">net</span><span class="p">:</span> <span class="n">MockNetwork</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">a</span><span class="p">:</span> <span class="n">MockNetwork</span><span class="p">.</span><span class="n">MockNode</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">b</span><span class="p">:</span> <span class="n">MockNetwork</span><span class="p">.</span><span class="n">MockNode</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">notary</span><span class="p">:</span> <span class="n">Party</span>
<span class="n">@Before</span>
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">net</span> <span class="p">=</span> <span class="n">MockNetwork</span><span class="p">()</span>
<span class="k">val</span> <span class="py">nodes</span> <span class="p">=</span> <span class="n">net</span><span class="p">.</span><span class="n">createSomeNodes</span><span class="p">()</span>
<span class="n">a</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">partyNodes</span><span class="p">[</span><span class="m">0</span><span class="p">]</span>
<span class="n">b</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">partyNodes</span><span class="p">[</span><span class="m">1</span><span class="p">]</span>
<span class="n">notary</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">notaryNode</span><span class="p">.</span><span class="n">info</span><span class="p">.</span><span class="n">notaryIdentity</span>
<span class="n">net</span><span class="p">.</span><span class="n">runNetwork</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">@After</span>
<span class="k">fun</span> <span class="nf">tearDown</span><span class="p">()</span> <span class="p">{</span>
<span class="n">net</span><span class="p">.</span><span class="n">stopNodes</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
</div>
<p>We create a mock network in our <code class="docutils literal"><span class="pre">&#64;Before</span></code> 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&#8217;re done.</p>
<p>Next, we write a test case:</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> @Test
fun `resolve from two hashes`() {
val (stx1, stx2) = makeTransactions()
val p = ResolveTransactionsFlow(setOf(stx2.id), a.info.legalIdentity)
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
val results = future.getOrThrow()
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
databaseTransaction(b.database) {
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id))
assertEquals(stx2, b.storage.validatedTransactions.getTransaction(stx2.id))
}
}
</pre></div>
</div>
<p>We&#8217;ll take a look at the <code class="docutils literal"><span class="pre">makeTransactions</span></code> function in a moment. For now, it&#8217;s enough to know that it returns two
<code class="docutils literal"><span class="pre">SignedTransaction</span></code> objects, the second of which spends the first. Both transactions are known by node A
but not node B.</p>
<p>The test logic is simple enough: we create the flow, giving it node A&#8217;s identity as the target to talk to.
Then we start it on node B and use the <code class="docutils literal"><span class="pre">net.runNetwork()</span></code> 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 node B&#8217;s database to see that the flow had the intended effect
on the node&#8217;s persistent state.</p>
<p>Here&#8217;s what <code class="docutils literal"><span class="pre">makeTransactions</span></code> looks like:</p>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> <span class="k">private</span> <span class="k">fun</span> <span class="nf">makeTransactions</span><span class="p">(</span><span class="n">signFirstTX</span><span class="p">:</span> <span class="n">Boolean</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span> <span class="n">withAttachment</span><span class="p">:</span> <span class="n">SecureHash</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span><span class="p">):</span> <span class="n">Pair</span><span class="p">&lt;</span><span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">SignedTransaction</span><span class="p">&gt;</span> <span class="p">{</span>
<span class="c1">// Make a chain of custody of dummy states and insert into node A.</span>
<span class="k">val</span> <span class="py">dummy1</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">DummyContract</span><span class="p">.</span><span class="n">generateInitial</span><span class="p">(</span><span class="n">MEGA_CORP</span><span class="p">.</span><span class="n">ref</span><span class="p">(</span><span class="m">1</span><span class="p">),</span> <span class="m">0</span><span class="p">,</span> <span class="n">notary</span><span class="p">).</span><span class="n">let</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">withAttachment</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">addAttachment</span><span class="p">(</span><span class="n">withAttachment</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">signFirstTX</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">MEGA_CORP_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">DUMMY_NOTARY_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">toSignedTransaction</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">val</span> <span class="py">dummy2</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">DummyContract</span><span class="p">.</span><span class="n">move</span><span class="p">(</span><span class="n">dummy1</span><span class="p">.</span><span class="n">tx</span><span class="p">.</span><span class="n">outRef</span><span class="p">(</span><span class="m">0</span><span class="p">),</span> <span class="n">MINI_CORP_PUBKEY</span><span class="p">).</span><span class="n">let</span> <span class="p">{</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">MEGA_CORP_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">DUMMY_NOTARY_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">toSignedTransaction</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">databaseTransaction</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">database</span><span class="p">)</span> <span class="p">{</span>
<span class="n">a</span><span class="p">.</span><span class="n">services</span><span class="p">.</span><span class="n">recordTransactions</span><span class="p">(</span><span class="n">dummy1</span><span class="p">,</span> <span class="n">dummy2</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">Pair</span><span class="p">(</span><span class="n">dummy1</span><span class="p">,</span> <span class="n">dummy2</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
</div>
<p>We&#8217;re using the <code class="docutils literal"><span class="pre">DummyContract</span></code>, 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&#8217;t do anything else. This code simply creates a transaction that issues a dummy state (the issuer is
<code class="docutils literal"><span class="pre">MEGA_CORP</span></code>, a pre-defined unit test identity), signs it with the test notary and MegaCorp keys and then
converts the builder to the final <code class="docutils literal"><span class="pre">SignedTransaction</span></code>. 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 node A by sending them
directly to the <code class="docutils literal"><span class="pre">a.services.recordTransaction</span></code> method (note that this method doesn&#8217;t check the transactions are
valid) inside a <code class="docutils literal"><span class="pre">databaseTransaction</span></code>. 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.</p>
<p>And that&#8217;s it: you can explore the documentation for the <a class="reference external" href="api/kotlin/corda/net.corda.testing.node/-mock-network/index.html">MockNetwork API</a> here.</p>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="running-a-notary.html" class="btn btn-neutral float-right" title="Running a notary service" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="flow-state-machines.html" class="btn btn-neutral" title="Writing flows" 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>