github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-059-test-scopes.md (about) 1 # ADR 059: Test Scopes 2 3 ## Changelog 4 5 * 2022-08-02: Initial Draft 6 * 2023-03-02: Add precision for integration tests 7 * 2023-03-23: Add precision for E2E tests 8 9 ## Status 10 11 PROPOSED Partially Implemented 12 13 ## Abstract 14 15 Recent work in the SDK aimed at breaking apart the monolithic root go module has highlighted 16 shortcomings and inconsistencies in our testing paradigm. This ADR clarifies a common 17 language for talking about test scopes and proposes an ideal state of tests at each scope. 18 19 ## Context 20 21 [ADR-053: Go Module Refactoring](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-053-go-module-refactoring.md) expresses our desire for an SDK composed of many 22 independently versioned Go modules, and [ADR-057: App Wiring](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-057-app-wiring.md) offers a methodology 23 for breaking apart inter-module dependencies through the use of dependency injection. As 24 described in [EPIC: Separate all SDK modules into standalone go modules](https://github.com/cosmos/cosmos-sdk/issues/11899), module 25 dependencies are particularly complected in the test phase, where simapp is used as 26 the key test fixture in setting up and running tests. It is clear that the successful 27 completion of Phases 3 and 4 in that EPIC require the resolution of this dependency problem. 28 29 In [EPIC: Unit Testing of Modules via Mocks](https://github.com/cosmos/cosmos-sdk/issues/12398) it was thought this Gordian knot could be 30 unwound by mocking all dependencies in the test phase for each module, but seeing how these 31 refactors were complete rewrites of test suites discussions began around the fate of the 32 existing integration tests. One perspective is that they ought to be thrown out, another is 33 that integration tests have some utility of their own and a place in the SDK's testing story. 34 35 Another point of confusion has been the current state of CLI test suites, [x/auth](https://github.com/cosmos/cosmos-sdk/blob/0f7e56c6f9102cda0ca9aba5b6f091dbca976b5a/x/auth/client/testutil/suite.go#L44-L49) for 36 example. In code these are called integration tests, but in reality function as end to end 37 tests by starting up a tendermint node and full application. [EPIC: Rewrite and simplify 38 CLI tests](https://github.com/cosmos/cosmos-sdk/issues/12696) identifies the ideal state of CLI tests using mocks, but does not address the 39 place end to end tests may have in the SDK. 40 41 From here we identify three scopes of testing, **unit**, **integration**, **e2e** (end to 42 end), seek to define the boundaries of each, their shortcomings (real and imposed), and their 43 ideal state in the SDK. 44 45 ### Unit tests 46 47 Unit tests exercise the code contained in a single module (e.g. `/x/bank`) or package 48 (e.g. `/client`) in isolation from the rest of the code base. Within this we identify two 49 levels of unit tests, *illustrative* and *journey*. The definitions below lean heavily on 50 [The BDD Books - Formulation](https://leanpub.com/bddbooks-formulation) section 1.3. 51 52 *Illustrative* tests exercise an atomic part of a module in isolation - in this case we 53 might do fixture setup/mocking of other parts of the module. 54 55 Tests which exercise a whole module's function with dependencies mocked, are *journeys*. 56 These are almost like integration tests in that they exercise many things together but still 57 use mocks. 58 59 Example 1 journey vs illustrative tests - [depinject's BDD style tests](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/features/bindings.feature), show how we can 60 rapidly build up many illustrative cases demonstrating behavioral rules without [very much code](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/binding_test.go) while maintaining high level readability. 61 62 Example 2 [depinject table driven tests](https://github.com/cosmos/cosmos-sdk/blob/main/depinject/provider_desc_test.go) 63 64 Example 3 [Bank keeper tests](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/x/bank/keeper/keeper_test.go#L94-L105) - A mock implementation of `AccountKeeper` is supplied to the keeper constructor. 65 66 #### Limitations 67 68 Certain modules are tightly coupled beyond the test phase. A recent dependency report for 69 `bank -> auth` found 274 total usages of `auth` in `bank`, 50 of which are in 70 production code and 224 in test. This tight coupling may suggest that either the modules 71 should be merged, or refactoring is required to abstract references to the core types tying 72 the modules together. It could also indicate that these modules should be tested together 73 in integration tests beyond mocked unit tests. 74 75 In some cases setting up a test case for a module with many mocked dependencies can be quite 76 cumbersome and the resulting test may only show that the mocking framework works as expected 77 rather than working as a functional test of interdependent module behavior. 78 79 ### Integration tests 80 81 Integration tests define and exercise relationships between an arbitrary number of modules 82 and/or application subsystems. 83 84 Wiring for integration tests is provided by `depinject` and some [helper code](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/testutil/sims/app_helpers.go#L95) starts up 85 a running application. A section of the running application may then be tested. Certain 86 inputs during different phases of the application life cycle are expected to produce 87 invariant outputs without too much concern for component internals. This type of black box 88 testing has a larger scope than unit testing. 89 90 Example 1 [client/grpc_query_test/TestGRPCQuery](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/client/grpc_query_test.go#L111-L129) - This test is misplaced in `/client`, 91 but tests the life cycle of (at least) `runtime` and `bank` as they progress through 92 startup, genesis and query time. It also exercises the fitness of the client and query 93 server without putting bytes on the wire through the use of [QueryServiceTestHelper](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/baseapp/grpcrouter_helpers.go#L31). 94 95 Example 2 `x/evidence` Keeper integration tests - Starts up an application composed of [8 96 modules](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/x/evidence/testutil/app.yaml#L1) with [5 keepers](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/x/evidence/keeper/keeper_test.go#L101-L106) used in the integration test suite. One test in the suite 97 exercises [HandleEquivocationEvidence](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/x/evidence/keeper/infraction_test.go#L42) which contains many interactions with the staking 98 keeper. 99 100 Example 3 - Integration suite app configurations may also be specified via golang (not 101 YAML as above) [statically](https://github.com/cosmos/cosmos-sdk/blob/main/x/nft/testutil/app_config.go) or [dynamically](https://github.com/cosmos/cosmos-sdk/blob/8c23f6f957d1c0bedd314806d1ac65bea59b084c/tests/integration/bank/keeper/keeper_test.go#L129-L134). 102 103 #### Limitations 104 105 Setting up a particular input state may be more challenging since the application is 106 starting from a zero state. Some of this may be addressed by good test fixture 107 abstractions with testing of their own. Tests may also be more brittle, and larger 108 refactors could impact application initialization in unexpected ways with harder to 109 understand errors. This could also be seen as a benefit, and indeed the SDK's current 110 integration tests were helpful in tracking down logic errors during earlier stages 111 of app-wiring refactors. 112 113 ### Simulations 114 115 Simulations (also called generative testing) are a special case of integration tests where 116 deterministically random module operations are executed against a running simapp, building 117 blocks on the chain until a specified height is reached. No *specific* assertions are 118 made for the state transitions resulting from module operations but any error will halt and 119 fail the simulation. Since `crisis` is included in simapp and the simulation runs 120 EndBlockers at the end of each block any module invariant violations will also fail 121 the simulation. 122 123 Modules must implement [AppModuleSimulation.WeightedOperations](https://github.com/cosmos/cosmos-sdk/blob/2bec9d2021918650d3938c3ab242f84289daef80/types/module/simulation.go#L31) to define their 124 simulation operations. Note that not all modules implement this which may indicate a 125 gap in current simulation test coverage. 126 127 Modules not returning simulation operations: 128 129 * `auth` 130 * `evidence` 131 * `mint` 132 * `params` 133 134 A separate binary, [runsim](https://github.com/cosmos/tools/tree/master/cmd/runsim), is responsible for kicking off some of these tests and 135 managing their life cycle. 136 137 #### Limitations 138 139 * [A success](https://github.com/cosmos/cosmos-sdk/runs/7606931983?check_suite_focus=true) may take a long time to run, 7-10 minutes per simulation in CI. 140 * [Timeouts](https://github.com/cosmos/cosmos-sdk/runs/7606932295?check_suite_focus=true) sometimes occur on apparent successes without any indication why. 141 * Useful error messages not provided on [failure](https://github.com/cosmos/cosmos-sdk/runs/7606932548?check_suite_focus=true) from CI, requiring a developer to run 142 the simulation locally to reproduce. 143 144 ### E2E tests 145 146 End to end tests exercise the entire system as we understand it in as close an approximation 147 to a production environment as is practical. Presently these tests are located at 148 [tests/e2e](https://github.com/cosmos/cosmos-sdk/tree/main/tests/e2e) and rely on [testutil/network](https://github.com/cosmos/cosmos-sdk/tree/main/testutil/network) to start up an in-process Tendermint node. 149 150 An application should be built as minimally as possible to exercise the desired functionality. 151 The SDK uses an application will only the required modules for the tests. The application developer is adviced to use its own application for e2e tests. 152 153 #### Limitations 154 155 In general the limitations of end to end tests are orchestration and compute cost. 156 Scaffolding is required to start up and run a prod-like environment and the this 157 process takes much longer to start and run than unit or integration tests. 158 159 Global locks present in Tendermint code cause stateful starting/stopping to sometimes hang 160 or fail intermittently when run in a CI environment. 161 162 The scope of e2e tests has been complected with command line interface testing. 163 164 ## Decision 165 166 We accept these test scopes and identify the following decisions points for each. 167 168 | Scope | App Type | Mocks? | 169 | ----------- | ------------------- | ------ | 170 | Unit | None | Yes | 171 | Integration | integration helpers | Some | 172 | Simulation | minimal app | No | 173 | E2E | minimal app | No | 174 175 The decision above is valid for the SDK. An application developer should test their application with their full application instead of the minimal app. 176 177 ### Unit Tests 178 179 All modules must have mocked unit test coverage. 180 181 Illustrative tests should outnumber journeys in unit tests. 182 183 Unit tests should outnumber integration tests. 184 185 Unit tests must not introduce additional dependencies beyond those already present in 186 production code. 187 188 When module unit test introduction as per [EPIC: Unit testing of modules via mocks](https://github.com/cosmos/cosmos-sdk/issues/12398) 189 results in a near complete rewrite of an integration test suite the test suite should be 190 retained and moved to `/tests/integration`. We accept the resulting test logic 191 duplication but recommend improving the unit test suite through the addition of 192 illustrative tests. 193 194 ### Integration Tests 195 196 All integration tests shall be located in `/tests/integration`, even those which do not 197 introduce extra module dependencies. 198 199 To help limit scope and complexity, it is recommended to use the smallest possible number of 200 modules in application startup, i.e. don't depend on simapp. 201 202 Integration tests should outnumber e2e tests. 203 204 ### Simulations 205 206 Simulations shall use a minimal application (usually via app wiring). They are located under `/x/{moduleName}/simulation`. 207 208 ### E2E Tests 209 210 Existing e2e tests shall be migrated to integration tests by removing the dependency on the 211 test network and in-process Tendermint node to ensure we do not lose test coverage. 212 213 The e2e rest runner shall transition from in process Tendermint to a runner powered by 214 Docker via [dockertest](https://github.com/ory/dockertest). 215 216 E2E tests exercising a full network upgrade shall be written. 217 218 The CLI testing aspect of existing e2e tests shall be rewritten using the network mocking 219 demonstrated in [PR#12706](https://github.com/cosmos/cosmos-sdk/pull/12706). 220 221 ## Consequences 222 223 ### Positive 224 225 * test coverage is increased 226 * test organization is improved 227 * reduced dependency graph size in modules 228 * simapp removed as a dependency from modules 229 * inter-module dependencies introduced in test code are removed 230 * reduced CI run time after transitioning away from in process Tendermint 231 232 ### Negative 233 234 * some test logic duplication between unit and integration tests during transition 235 * test written using dockertest DX may be a bit worse 236 237 ### Neutral 238 239 * some discovery required for e2e transition to dockertest 240 241 ## Further Discussions 242 243 It may be useful if test suites could be run in integration mode (with mocked tendermint) or 244 with e2e fixtures (with real tendermint and many nodes). Integration fixtures could be used 245 for quicker runs, e2e fixures could be used for more battle hardening. 246 247 A PoC `x/gov` was completed in PR [#12847](https://github.com/cosmos/cosmos-sdk/pull/12847) 248 is in progress for unit tests demonstrating BDD [Rejected]. 249 Observing that a strength of BDD specifications is their readability, and a con is the 250 cognitive load while writing and maintaining, current consensus is to reserve BDD use 251 for places in the SDK where complex rules and module interactions are demonstrated. 252 More straightforward or low level test cases will continue to rely on go table tests. 253 254 Levels are network mocking in integration and e2e tests are still being worked on and formalized.