blob: 1a4f8a461fc2635c2c3fe6bc9b2741569cbf762c [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>PLC4X &#x2013; </title>
<script src="../../js/jquery.slim.min.js" type="text/javascript"></script>
<!--script src="../../js/popper.min.js" type="javascript"></script-->
<script src="../../js/bootstrap.bundle.min.js" type="text/javascript"></script>
<!-- The tooling for adding images and links to Apache events -->
<script src="https://www.apachecon.com/event-images/snippet.js" type="text/javascript"></script>
<!-- FontAwesome -->
<link rel="stylesheet" href="../../css/all.min.css" type="text/css"/>
<!-- Bootstrap -->
<link rel="stylesheet" href="../../css/bootstrap.min.css" type="text/css"/>
<!-- Some Maven Site defaults -->
<link rel="stylesheet" href="../../css/maven-base.css" type="text/css"/>
<link rel="stylesheet" href="../../css/maven-theme.css" type="text/css"/>
<!-- The PLC4X version of a bootstrap theme -->
<link rel="stylesheet" href="../../css/themes/plc4x.css" type="text/css" id="pagestyle"/>
<!-- A custom style for printing content -->
<link rel="stylesheet" href="../../css/print.css" type="text/css" media="print"/>
<meta http-equiv="Content-Language" content="en"/>
</head>
<body class="composite">
<nav class="navbar navbar-light navbar-expand-md bg-faded justify-content-center border-bottom">
<!--a href="/" class="navbar-brand d-flex w-50 mr-auto">Navbar 3</a-->
<a href="https://plc4x.apache.org/" id="bannerLeft"><img src="../../images/apache_plc4x_logo_small.png" alt="Apache PLC4X"/></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsingNavbar3">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse w-100" id="collapsingNavbar3">
<ul class="navbar-nav w-100 justify-content-center">
<li class="nav-item">
<a class="nav-link" href="../../index.html">Home</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="../../users/index.html">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="../../developers/index.html">Developers</a>
</li>
<li class="nav-item">
<a class="nav-link" href="../../apache/index.html">Apache</a>
</li>
</ul>
<ul class="nav navbar-nav ml-auto justify-content-end">
<li class="nav-item row valign-middle">
<a class="acevent" data-format="wide" data-mode="light" data-event="random" style="width:240px;height:60px;"></a>
</li>
</ul>
</div>
</nav>
<div class="container-fluid">
<div class="row h-100">
<nav class="col-sm-push col-md-2 pt-3 sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a href="../../users/index.html" class="nav-link">Section Home</a>
</li>
<li class="nav-item">
<a href="../../users/download.html" class="nav-link">Download</a>
</li>
<li class="nav-item">
<a href="../../users/adopters.html" class="nav-link">Adopters</a>
</li>
<li class="nav-item">
<a href="../../users/commercial-support.html" class="nav-link">Commercial support</a>
</li>
<li class="nav-item">
<a href="../../users/gettingstarted.html" class="nav-link">Getting Started</a>
<ul class="flex-column pl-4 nav">
<li class="nav-item">
<a href="../../users/getting-started/plc4go.html" class="nav-link">Go</a>
</li>
<li class="nav-item">
<a href="../../users/getting-started/plc4j.html" class="nav-link">Java</a>
</li>
<li class="nav-item">
<a href="../../users/getting-started/using-snapshots.html" class="nav-link">Using SNAPSHOTS</a>
</li>
<li class="nav-item">
<a href="../../users/getting-started/general-concepts.html" class="nav-link">General Concepts</a>
</li>
<li class="nav-item">
<a href="../../users/getting-started/virtual-modbus.html" class="nav-link">Virtual Modbus</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="../../users/blogs-videos-and-slides.html" class="nav-link">Blogs, Videos and Slides</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/index.html" class="nav-link">Protocols</a>
<ul class="flex-column pl-4 nav">
<li class="nav-item">
<a href="../../users/protocols/ab-eth.html" class="nav-link">AB-ETH</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/ads.html" class="nav-link">ADS/AMS</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/bacnetip.html" class="nav-link">BACnet/IP</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/canopen.html" class="nav-link">CANopen</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/deltav.html" class="nav-link">DeltaV</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/df1.html" class="nav-link">DF1</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/ethernet-ip.html" class="nav-link">EtherNet/IP</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/firmata.html" class="nav-link">Firmata</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/knxnetip.html" class="nav-link">KNXnet/IP</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/modbus.html" class="nav-link">Modbus</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/opc-ua.html" class="nav-link">OPC UA</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/plc4x.html" class="nav-link">PLC4X (Proxy)</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/s7.html" class="nav-link">S7 (Step7)</a>
</li>
<li class="nav-item">
<a href="../../users/protocols/simulated.html" class="nav-link">Simulated</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="../../users/transports/index.html" class="nav-link">Transports</a>
<ul class="flex-column pl-4 nav">
<li class="nav-item">
<a href="../../users/transports/tcp.html" class="nav-link">TCP</a>
</li>
<li class="nav-item">
<a href="../../users/transports/udp.html" class="nav-link">UDP</a>
</li>
<li class="nav-item">
<a href="../../users/transports/serial.html" class="nav-link">Serial</a>
</li>
<li class="nav-item">
<a href="../../users/transports/socketcan.html" class="nav-link">SocketCAN</a>
</li>
<li class="nav-item">
<a href="../../users/transports/raw-socket.html" class="nav-link">Raw Socket</a>
</li>
<li class="nav-item">
<a href="../../users/transports/pcap-replay.html" class="nav-link">PCAP Replay</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="../../users/integrations/index.html" class="nav-link">Integrations</a>
<ul class="flex-column pl-4 nav">
<li class="nav-item">
<a href="../../users/integrations/apache-calcite.html" class="nav-link">Apache Calcite</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/apache-camel.html" class="nav-link">Apache Camel</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/apache-edgent.html" class="nav-link">Apache Edgent</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/apache-iotdb.html" class="nav-link">Apache IoTDB</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/apache-kafka.html" class="nav-link">Apache Kafka</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/apache-nifi.html" class="nav-link">Apache NiFi</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/apache-streampipes.html" class="nav-link">Apache StreamPipes</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/eclipse-ditto.html" class="nav-link">Eclipse Ditto</a>
</li>
<li class="nav-item">
<a href="../../users/integrations/eclipse-milo.html" class="nav-link">Eclipse Milo OPC UA Server</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="../../users/tools/index.html" class="nav-link">Tools</a>
<ul class="flex-column pl-4 nav">
<li class="nav-item">
<a href="../../users/tools/capture-replay.html" class="nav-link">Capture Replay</a>
</li>
<li class="nav-item">
<a href="../../users/tools/connection-pool.html" class="nav-link">Connection Pool</a>
</li>
<li class="nav-item">
<a href="../../users/tools/connection-cache.html" class="nav-link">Connection Cache</a>
</li>
<li class="nav-item">
<a href="../../users/tools/opm.html" class="nav-link">Object PLC Mapping (OPM)</a>
</li>
<li class="nav-item">
<a href="../../users/tools/scraper.html" class="nav-link">Scraper</a>
</li>
<li class="nav-item">
<strong class="nav-link">PLC4X without a PLC and Unit Testing</strong>
</li>
</ul>
</li>
<li class="nav-item">
<a href="../../users/industry40.html" class="nav-link">Industry 4.0 with Apache</a>
</li>
<li class="nav-item">
<a href="../../users/security.html" class="nav-link">Security</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="ml-sm-auto px-4 col-sm-pull col-md-9 col-lg-10 h-100">
<div class="sect1">
<h2 id="testing_or_using_plc4x_without_a_plc">Testing (or using PLC4X without a PLC)</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="the_mock_driver">The Mock Driver</h3>
<div class="paragraph">
<p>PLC4X has a <em>Mock Driver</em> which was initially implemented to be used for Unit Tests and this still is its main purpose.
But this driver is also very suitable to play around a bit with the PLC4X API if no <em>Hardware</em> PLC is available.
The driver can be found in the Maven module</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>&lt;dependency&gt;
&lt;groupId&gt;org.apache.plc4x&lt;/groupId&gt;
&lt;artifactId&gt;plc4j-transport-test&lt;/artifactId&gt;
&lt;version&gt;${current.version}&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The connection string Syntax for the mock driver is <code>mock:{name-of-the-connection}</code>. So you can use multiple Mock Devices at the same time.</p>
</div>
<div class="paragraph">
<p>The Mock Driver does nothing else than forwarding all Requests to a <em>Virtual Device</em> which we can provide to control all responses and also Monitor them, e.g. for unit tests.
The Interface for the Mock Device is</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>public interface MockDevice {
Pair&lt;PlcResponseCode, PlcValue&gt; read(String fieldQuery);
PlcResponseCode write(String fieldQuery, Object value);
Pair&lt;PlcResponseCode, PlcSubscriptionHandle&gt; subscribe(String fieldQuery);
void unsubscribe();
// ...
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="simple_example">Simple Example</h3>
<div class="paragraph">
<p>Imagine we have some Code which we cannot control or whose functionality we want to test.
This can be done with the Mock Driver in the following way.</p>
</div>
<div class="paragraph">
<p>First, a new Mock Connection is established (like any other connection also would be):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>PlcDriverManager driverManager = new PlcDriverManager();
PlcMockConnection connection = (PlcMockConnection) driverManager.getConnection("mock:my-mock-connection");</code></pre>
</div>
</div>
<div class="paragraph">
<p>You see, that we directly cast the Connection to a <code>PlcMockConnection</code>. This is done, because we need to <em>connect</em> a Device to this Mock Connection.</p>
</div>
<div class="paragraph">
<p>This is done in the following Snippet</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>connection.setDevice(mockDevice);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here, we pass it an instance of <code>MockDevice</code> which could be a simple Implementation of the interface like</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>MockDevice mockDevice = new MockDevice() {
Pair&lt;PlcResponseCode, PlcValue&gt; read(String fieldQuery) {
System.out.println("I got a read to " + fieldQuery);
return Pair.of(PlcResponseCode.OK, new PlcString("hello"));
}
PlcResponseCode write(String fieldQuery, Object value) {
System.out.println("I got a write to " + fieldQuery + " with the value " + value);
return PlcResponseCode.OK;
}
// ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This would just return a String Value <code>hello</code> for every request and print all read and write requests to the Console.</p>
</div>
</div>
<div class="sect2">
<h3 id="unit_testing_with_the_mock_driver">Unit Testing with the Mock Driver</h3>
<div class="paragraph">
<p>To use the Mock driver in Unit Tests the easiest way is to generate the <code>MockDriver</code> instance as Mockito (or any other Framework) Mock.
Like in the following Example</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>MockDevice mockDevice = Mockito.mock(MockDevice.class);
PlcDriverManager driverManager = new PlcDriverManager();
PlcMockConnection connection = (PlcMockConnection) driverManager.getConnection("mock:my-mock-connection");
connection.setDevice(mockDevice);
// Populate the Mock to avoid a NPE
when(mockDevice).read(anyString()).thenReturn(Pair.of(PlcResponseCode.OK, new PlcString("hello")));
// Some Demo code that uses the same Driver Manager and either the connection from above
// or at least mock:my-mock-connection as connection string
// Here: send a request to the field "MyAdress"
connection
.readRequestBuilder
.addItem("station", "MyAdress")
.build()
.execute()
.get();
// Check that the we really issued a Read request to the Field "MyAdress"
verify(mockDevice).read(eq("MyAdress"));</code></pre>
</div>
</div>
<div class="paragraph">
<p>But as the <em>MockDriver</em> uses a static Mock Connection registry the following Code works also</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code>MockDevice mockDevice = Mockito.mock(MockDevice.class);
// Setup
PlcDriverManager driverManager = new PlcDriverManager();
PlcMockConnection connection = (PlcMockConnection) driverManager.getConnection("mock:my-mock-connection");
connection.setDevice(mockDevice);
// Populate the Mock to avoid a NPE
when(mockDevice).read(anyString()).thenReturn(Pair.of(PlcResponseCode.OK, new PlcString("hello")));
// Some Demo code that uses the same Driver Manager and either the connection from above
// or at least mock:my-mock-connection as connection string
// Here: send a request to the field "MyAdress"
// and we build up a new Connection
try (PlcConnection conn = driverManager.getConnection("mock:my-mock-connection")) {
conn
.readRequestBuilder
.addItem("station", "MyAdress")
.build()
.execute()
.get();
} catch (Exception e) {
// do nothing
}
// Check that the we really issued a Read request to the Field "MyAdress"
verify(mockDevice).read(eq("MyAdress"));</code></pre>
</div>
</div>
<div class="paragraph">
<p>The Snippet above shows that the part under test really has to share nothing with the test code except for the connection string.</p>
</div>
</div>
<div class="sect2">
<h3 id="conclusion">Conclusion</h3>
<div class="paragraph">
<p>The above examples show that the <code>MockDriver</code> driver can not only be used to play around with the API but is also a powerful tool to
do unit testing of Code which uses the PLC4X API.
All that needs to be done is to either pass an instance of the Mocked Connection or just use the same Connection string (e.g. from a test configuration) that was used to Prepare a Mock Device.
Some Examples of futher (more Complex) use cases can be found in the PLC4X Codebases, e.g. in the following classes</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>org.apache.plc4x.java.opm.PlcEntityManagerTest</code></p>
</li>
<li>
<p><code>org.apache.plc4x.java.opm.PlcEntityManagerComplexTest</code></p>
</li>
<li>
<p><code>org.apache.plc4x.java.scraper.ScraperTest</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>and many more Test classes, especially in the OPM and the Scraper Module.</p>
</div>
</div>
</div>
</div>
</main>
<footer class="pt-4 my-md-5 pt-md-5 w-100 border-top">
<div class="row justify-content-md-center" style="font-size: 13px">
<div class="col col-6 text-center">
Copyright &#169; 2017&#x2013;2022 <a href="https://www.apache.org/">The Apache Software Foundation</a>.
All rights reserved.<br/>
Apache PLC4X, PLC4X, Apache, the Apache feather logo, and the Apache PLC4X project logo are either registered trademarks or trademarks of The Apache Software Foundation in the United States and other countries. All other marks mentioned may be trademarks or registered trademarks of their respective owners.
<br/><div style="text-align:center;">Home screen image taken from <a
href="https://flic.kr/p/chEftd">Flickr</a>, "Tesla Robot Dance" by Steve Jurvetson, licensed
under <a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0 Generic</a>, image cropped
and blur effect added.</div>
</div>
</div>
</footer>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="../../js/jquery.slim.min.js"></script>
<script src="../../js/popper.min.js"></script>
<script src="../../js/bootstrap.min.js"></script>
<script type="text/javascript">
$('.carousel .carousel-item').each(function(){
var next = $(this).next();
if (!next.length) {
next = $(this).siblings(':first');
}
next.children(':first-child').clone().appendTo($(this));
for (let i = 0; i < 3; i++) {
next=next.next();
if (!next.length) {
next = $(this).siblings(':first');
}
next.children(':first-child').clone().appendTo($(this));
}
});
</script>
</body>
</html>