Skip to content

Commit 7b7bf98

Browse files
authored
decide on test granularity (#104)
A decision is suggested to cope with currently experienced limitations (platformio/platformio-core#4849 (comment)) of PlatformIO.
2 parents 78e0770 + dfae652 commit 7b7bf98

2 files changed

Lines changed: 142 additions & 1 deletion

File tree

doc/decisions/dr-001.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
DR001 Represent Software Architecture in Directory Structure
1+
DR001 Represent Software Architecture in Directory Structure {#dr_001}
22
============================================================
33

44
Context

doc/decisions/dr-003.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# DR003 Test Granularity and Stubbing Challenges {#dr_003}
2+
3+
## Context
4+
5+
Our software development project encounters challenges in designing unit tests due to issues with test granularity and stubbing dependencies.
6+
7+
Note that we structure our software into packages (see
8+
[Implementing a Plug-in Architecture](<\ref plugin_architecture> "Implementing a Plug-in Architecture"))
9+
which are technically implemented as [(private) libraries][3] (see
10+
[Root Directory for the Package's source files](<\ref dr_002> "Root Directory for the Package's source files")
11+
).
12+
13+
[3]: https://docs.platformio.org/en/latest/projectconf/sections/platformio/options/directory/lib_dir.html#lib-dir "documentation of `lib_dir`"
14+
15+
## Challenges with Stubbing
16+
17+
Our initial assumption was to write unit tests for individual units.
18+
However, challenges arose, particularly regarding the granularity of "unit" tests envisioned.
19+
20+
### Option 0: Testing individual units {#dr_003_o0}
21+
22+
A unit in this context is, for example, a class or a translation unit.
23+
A test would be compiled along with the unit under test (UUT), linked together, and executed.
24+
The build configuration should be defined within the [`platformio.ini` configuration file][1].
25+
26+
A limitation of PlatformIO [has been experienced][7] specifically with test builds, where not only the UUT but the whole library in which it resides is included in the build (see [issue #4849][2]).
27+
This prevents stubbing dependencies of the UUT to other translation units within the same library, making it impossible to test individual units isolated from others this way.
28+
29+
[1]: https://docs.platformio.org/en/latest/projectconf/#platformio-ini-project-configuration-file
30+
[2]: https://github.com/platformio/platformio-core/issues/4849 "Library Dependency Finder (LDF) adds too many files when testing (pio test)"
31+
[7]: https://community.platformio.org/t/partial-compilation-of-private-libraries-components-while-testing-for-different-environments/37079 "Partial compilation of private libraries/components (while testing) for different environments"
32+
33+
### Option 1: Filter source files {#dr_003_o1}
34+
35+
To overcome the limitations from
36+
[option 0](<\ref dr_003_o0> "option 0"),
37+
one can use a [`library.json`][JSON] manifest file for each library in combination with the [`extraScript` option][4] to filter files.
38+
This way, individual files could be filtered for specific build targets, although this approach is expected to be effortful, as filters have to be implemented for each individual unit.
39+
40+
[JSON]: https://docs.platformio.org/en/latest/manifests/library-json/index.html "PlatformIO documentation of manifest file"
41+
[4]: https://docs.platformio.org/en/latest/manifests/library-json/fields/build/extrascript.html "documentation of `extraScript`"
42+
43+
### Option 2: Split units into separate libraries {#dr_003_o2}
44+
45+
Another way to circumvent the limitations from
46+
[option 0](<\ref dr_003_o0> "option 0")
47+
would be to separate each unit into an individual library.
48+
This way, only the UUT would be added to the test script for the test build.
49+
However, a major disadvantage would be that the planned structuring of the software packages into directories would be lost, making the analysis of the software structure more difficult.
50+
51+
### Option 3: Test integrated units {#dr_003_o3}
52+
53+
Test features using units and their dependencies to a certain degree:
54+
55+
Either include (using [`lib_deps`][5] in [the project configuration][1]):
56+
57+
- **Option 3a):** all dependencies compatible with the test environment
58+
- **Option 3b):** all dependencies compatible with the test environment, but no
59+
[3rd party library adapters](<\ref third_party_adapters> "3rd party library adapters")
60+
- **Option 3c):** only those dependencies which inevitably come as part of the library in which the UUT resides
61+
62+
Omitted dependencies need to be substituted with stubs.
63+
64+
[5]: https://docs.platformio.org/en/latest/projectconf/sections/env/options/library/lib_deps.html "documentation of `lib_deps`"
65+
66+
## Comparison of Options {#dr_003_c1}
67+
68+
- **Option 0:** Testing individual units without additional build configuration files
69+
- Pros:
70+
- would enable keeping the test design as originally envisioned
71+
- errors in one unit would not affect other units
72+
- Cons:
73+
- currently **impossible** to implement
74+
- **Option 1:** Testing individual units with filters defined in additional files
75+
- Pros: same as Option 0
76+
- Cons:
77+
- implementation is **effortful and more complex**
78+
- **Option 2:** Testing individual units by separating them into individual libraries
79+
- Pros: same as Option 0
80+
- Cons:
81+
- the structuring of the software into packages could not be represented by directories anymore
82+
- **Option 3:** Testing integrated units
83+
- Pros:
84+
- fewer individual tests necessary
85+
- less prone to changes (of details irrelevant for the test scope)
86+
- fits better with the **Common Closure Principle**:
87+
Tests are defined outside of the libraries but in a [dedicated test directory][6].
88+
This is required by the build management tool currently used (PlatformIO).
89+
Interfaces of some units may change for other reasons than changes to the application requirements.
90+
For example when interfaces within a layer or to lower level layers change due to refactoring.
91+
These changes would require to adjust the tests.
92+
This means that the change in one layer would lead to a change in the "global" test directory.
93+
This effectively violates Common Closure Principle.
94+
- Cons:
95+
- to cover the same amount of control paths within the software with the integrated tests as with testing individual units, integrated test may require more complex or additional test cases.
96+
97+
[6]: https://docs.platformio.org/en/stable/projectconf/sections/platformio/options/directory/test_dir.html "documentation of `test_dir`"
98+
99+
### Comparing Options 3a)-3c) {#dr_003_c2}
100+
101+
- **Option 3a):** Testing integrated units including all compatible dependencies
102+
- Pros:
103+
- highest test coverage of options 3a)-3c)
104+
- Cons:
105+
- requires stubs of the 3rd party dependencies incompatible with the test environment
106+
- **Option 3b):** Testing integrated units including all compatible dependencies, excluding 3rd party adapters
107+
- Pros:
108+
- Does not depend on the interface of the 3rd party dependencies incompatible with the test environment.
109+
- Cons:
110+
- requires stubs of all 3rd party adapters
111+
- **Option 3c):** Testing integrated units of the libraries each
112+
- Pros:
113+
- Does not depend on the interface of the 3rd party dependencies incompatible with the test environment.
114+
- Cons:
115+
- requires the highest amount of stubs from the options 3a)-3c)
116+
117+
## Decision & Rationale {#dr_003_decision}
118+
119+
What do we want to achieve with the tests?
120+
To check if the observable behavior of the overall software is as expected.
121+
122+
As long as libraries are not delivered separately, it is not necessary to test them individually.
123+
124+
After careful consideration, we've decided on:
125+
126+
> **Option 3b):** Testing integrated units including all compatible dependencies, excluding 3rd party adapters
127+
128+
This decision aligns with the test objective, aiming to achieve good software quality while avoiding excessive effort.
129+
130+
This design decision shall be regarded as a minimum requirement.
131+
Additional tests or stubbing may be defined at one's liberty.
132+
133+
## Next Steps {#dr_003_next}
134+
135+
To implement this decision:
136+
137+
- Future tests are only required for the application layer.
138+
Units from lower layers will be tested indirectly.
139+
- Future tests may test several units at once.
140+
- Legacy tests that require modification must be checked if they satisfy the new test design and modified or removed accordingly.
141+

0 commit comments

Comments
 (0)