|
| 1 | +/** |
| 2 | +@page testing-style Testing - Unit & Integration Tests |
| 3 | + |
| 4 | +\tableofcontents |
| 5 | + |
| 6 | +\section ts-unit Unit Tests |
| 7 | + |
| 8 | +One of the design goals of the Framework was to facilitate full testing coverage of any detectors. DetectorGraph is designed so that each Detector is tested as a Unit - by passing specific input `TopicStates` and comparing output `TopicStates` with expected values. |
| 9 | +@warning Before continuing it may be good to familiarize yourself with the first few [examples](@ref custom_examples) as this document builds on top of those examples. |
| 10 | + |
| 11 | +\subsection ts-unit-siso Basics |
| 12 | + |
| 13 | +The most basic case is for a SISO Detector like the one in the [HelloWorld Example](@ref helloworld.cpp). Detector unit tests should be written following the Arrange-Act-Assert (AAA) unit testing pattern. |
| 14 | + |
| 15 | +_Arrange_ for the test consists of a graph instance with only the detector under test in it. In addition to that it's also cursory to grab a pointer `outTopic` to the output Topics we will check in the Assert section of the test. This help setting expectations for the reader and cleans up the later sections of the test. |
| 16 | +@snippetlineno helloworld.cpp UnitTest-Above-1 |
| 17 | +Note that different Unit Testing frameworks will use different function signatures for tests so adapt it as necessary. |
| 18 | + |
| 19 | +_Act_ consists in _Pushing_ the input data topic into the input queue and _Evaluating_ the graph. |
| 20 | +@snippetlineno helloworld.cpp UnitTest-Above-2 |
| 21 | + |
| 22 | +_Assert_ consists in [inspecting](@ref topic_inspection_apis) the output topic(s): |
| 23 | +@snippetlineno helloworld.cpp UnitTest-Above-3 |
| 24 | +Different Unit Testing frameworks will use different & more expressive assert methods but the idea is the same. |
| 25 | + |
| 26 | +\subsection ts-unit-siso-aaa-aa Act-Assert sequences |
| 27 | + |
| 28 | +In some cases it's nice to submit a detector to a small sequence of Act/Asserts. Care should be taken to maintain the test focused on a single behavior and not let it balloon out of proportion (testing more than one single behavior) - so this only applies when the behavior being tested is sequential in nature. |
| 29 | + |
| 30 | +One such case is to test the counting behavior of the [CounterWithReset Example](@ref counterwithreset.cpp). |
| 31 | +The test starts as before: |
| 32 | +@snippetlineno counterwithreset.cpp UnitTest-Count-1 |
| 33 | + |
| 34 | +And follows with an extra _Act_ and _Assert_ pair: |
| 35 | +@snippetlineno counterwithreset.cpp UnitTest-Count-2 |
| 36 | +Note that the middle _Assert_ is not necessary per se - it is helpful when it gives the reader confidence that _things are going as expected_ but they will be *very* unhelpful if they distract the reader from what's coming next; one or a couple of assert lines are fine, 10 are not. Use your best judgment here. |
| 37 | + |
| 38 | +\subsection ts-unit-miso Multiple Input Single Output |
| 39 | + |
| 40 | +Most times when testing a detector with multiple inputs and multiple outputs each input has to be _Pushed_ and _Evaluated_ on its own. |
| 41 | +Again, for the [CounterWithReset Example](@ref counterwithreset.cpp): |
| 42 | +@snippetlineno counterwithreset.cpp UnitTest-ResetCount-1 |
| 43 | +Here the _Arrange_ step includes pushing some data and evaluating the graph. |
| 44 | + |
| 45 | +_Act_ and _Assert_ are then done just as before: |
| 46 | +@snippetlineno counterwithreset.cpp UnitTest-ResetCount-2 |
| 47 | + |
| 48 | +\subsection ts-unit-futpub Future Publishes |
| 49 | + |
| 50 | +In order for detectors to close feedback loops/cycles in the graph they must be [FuturePublisher](@ref DetectorGraph::FuturePublisher)s. |
| 51 | +The simplest example of this is shown in the `ResetDetector` within the [CounterWithReset Example](@ref counterwithreset.cpp). |
| 52 | +The interesting detail here is that Topics published with [PublishOnFutureEvaluation](@ref DetectorGraph::FuturePublisher::PublishOnFutureEvaluation) are, as the name suggests, published only in the next evaluation and that needs to be taken into account in the unit test. |
| 53 | + |
| 54 | +@warning See the section below for a note on [Lag](@ref DetectorGraph::Lag) for reasons why that's preferred than what's shown here. |
| 55 | + |
| 56 | +Initial _Arrange_ and _Act_ are as normal: |
| 57 | +@snippetlineno counterwithreset.cpp UnitTest-ResetDetected-1 |
| 58 | + |
| 59 | +But the test must check: |
| 60 | + - that the output is not immediately published |
| 61 | + - that the intended `TopicState` is published on the next evaluation. |
| 62 | + |
| 63 | +And that can be done with: |
| 64 | +@snippetlineno counterwithreset.cpp UnitTest-ResetDetected-2 |
| 65 | + |
| 66 | +The same idea applies when testing the negative complementary of such behaviors: |
| 67 | +@snippetlineno counterwithreset.cpp UnitTest-NoResetDetected-1 |
| 68 | + |
| 69 | +\subsection ts-unit-lag Lag |
| 70 | + |
| 71 | +[Lags](@ref DetectorGraph::Lag) are the more general, preferred & readable way to close loops in a graph - and one place they shine the shiniest is in unit tests; since Lags are Detectors themselves and the lagged Topic is just another Topic. That means unit tests do not need to keep track of future publishes as that's all handled outside that one detector. Unit tests then are trivial with respect to the loop: |
| 72 | + |
| 73 | +@snippetlineno fancyvendingmachine.cpp UnitTest-LowerUserBalanceOnSale |
| 74 | + |
| 75 | +@note There are two reasons why Lags didn't simply replace `FuturePublish`; legacy code uses FuturePublish extensively and Lags may a bit wasteful in deeply embedded (limited code space) targets. |
| 76 | + |
| 77 | + |
| 78 | +\section ts-system Integration Tests |
| 79 | + |
| 80 | +In order to test the integration between detectors one can instantiate a subset of detectors (up to the entire set) of an application and test it in the same way as unit tests - all you have to do is think of the whole thing as one single big detector. This test should then be stable regardless of how many detectors compose the solution in between the inputs and outputs. |
| 81 | + |
| 82 | +In integration tests it's important to _flush_ the graph input queue continuously when appropriate for the reasons described in @ref ts-unit-futpub . This can be seen in the example for [CounterWithReset](@ref counterwithreset.cpp) below on line 311: |
| 83 | + |
| 84 | +@snippetlineno counterwithreset.cpp UnitTest-CounterResetIntegration |
| 85 | + |
| 86 | +*/ |
| 87 | + |
| 88 | +/** |
| 89 | +@page seo-testing Testing |
| 90 | +See [Testing Style](@ref testing-style). |
| 91 | +*/ |
| 92 | + |
| 93 | +/** |
| 94 | +@page seo-unit-testing Unit Testing |
| 95 | +@copydoc seo-testing |
| 96 | +*/ |
| 97 | + |
| 98 | +/** |
| 99 | +@page seo-integ-testing Integration Testing |
| 100 | +@copydoc seo-testing |
| 101 | +*/ |
0 commit comments