1# Unit Testing 2 3The goal of a unit test is to improve code quality and assure future development 4or refactoring does not result in broken builds and functionality. Tests that 5require consistent updating when refactoring code are likely tightly coupled to 6the internals of the component. 7 8> Writing unit tests is a constant struggle between writing enough tests and not 9> writing too many. I call this the unit testing Goldilocks rule—not too many, 10> not too few, but just enough. Thousands of tests for a small application can 11> be as damaging to development time as no tests. 12> 13> -- <cite>Ed Yerburgh, Testing Vue Applications (New York: Manning 14> Publications, 2019)</cite> 15 16## Test Libraries 17 18The OpenBMC Web UI unit test framework uses the Jest test runner and relies on 19the following libraries: 20 21- @vue/cli-plugin-unit-jest 22- @vue/test-utils 23 24## Test specification location and naming conventions 25 26- Create the test files in the /tests/unit directory 27- The naming convention is to replicate the folder and component name 28 29### Examples 30 31- The AppHeader.vue single-file component's (SFC) spec file is named 32 `AppHeader.spec.js` 33- Create a global component like `PageSection.vue` in the `/tests/global` 34 directory with the name `PageSection.spec.js` 35- Create a mixin like BVToastMixin in the `/tests/mixins` directory with the 36 name `BVToastMixin.spec.js` Running Tests 37 38## Running Tests 39 40The `test:unit` script will run all the test suites. Until the integration of 41the test script with the continuous integration tool is complete, it needs to be 42run manually before pushing up code for review. If you are working on fixing a 43test that is failing, follow the guidelines for debugging a failed tests or 44fixing failed snapshot tests. 45 46### Debugging a failed test 47 48The `test:unit:debugger` script will help to debug failing tests using the 49Chrome Developer Tools. To debug a test: 50 511. Add a `debugger` statement in the specifications file 521. Run the unit test in debugger mode 531. Open the Chrome browser and go to `chrome://inspect` 54 55### Fixing failed snapshot tests 56 57The `test:update` script will update snapshot tests. If the UI has changed and 58the snapshot tests are failing, after manually verifying the UI changes, run the 59update script to update the snapshots. Running `test:update` can be dangerous, 60as it will update all snapshot tests. 61 62It is critical to verify all snapshot tests before running the update script. 63The easiest way is to run the unit test in watch mode, 64`npm run test:unit -- --watch` and verify each snapshot. 65 66## Guidelines 67 68- Avoid coupling test code to source code when testing functionality 69 - If test cases fail during refactoring, the test case may be tightly coupled 70 with the application structure. 71- A test should not break if the functionality it tests has not changed 72- To maintain test readability, only pass in the data needed for the test to 73 work in your mock object 74- Avoid the creation of side-effects whenever possible 75- There is no return on investment for testing presentational HTML 76- Use `shallowMount` rather than mount unless child component rendering is 77 required 78- Avoid leaky tests by using `localVue` for all plugin installs, for example, 79 when testing a plugin like Vuex 80 81## Components 82 83### What to test 84 851. Test the function's inputs and outputs 86 - Test only dynamically generated output 87 - Test only output that is part of the component contract 881. Test any side-effects 891. Test correct rendering using a snapshot test 90 91### What not to test 92 931. Don't test third-party functionality 941. Don't test the internals of your components or that specific functions are 95 called. This can lead to unnecessary refactoring. 961. Don't go beyond the input and outputs of the component 971. Don't test the functionality of other libraries 981. Static components do not need unit tests, use snapshot testing 99 100### Strategy 101 1021. Define a component contract that is based upon the component API 1031. Create smaller functions with a specific purpose to make testing easier 1041. Test the component API by writing tests first and then writing code to fix 105 the tests 1061. Add a snapshot test once the presentational layer is validated through manual 107 visual testing 108 109### Snapshot Testing 110 111A snapshot test is a comparison of the code from two different points in time. 112When the view is rendering as expected, a snapshot is taken and when the test 113suite is run, this snapshot is compared to the current code to make sure nothing 114has changed. 115 116This type of testing is good for testing that static content output has not 117changed due to any code updates or refactoring. Too many snapshots can slow down 118development during refactors. Typically, these are written once the UI 119presentational layer is complete and validated. 120 121## Vuex Store 122 123There are two testing strategies for testing a Vuex store, which include testing 124store parts separately or testing a running store instance. Each strategy has 125its pros and cons. Given the size of the store and the number of developers that 126could potentially contribute to the project, the suggested strategy is to 127`test store parts separately`. 128 129### Testing Store Parts Separately 130 131Testing the parts separately is easy since each of the parts is a JavaScript 132function. Store parts to test include `actions`, `getters`, and `mutations`. 133 134#### Actions 135 136Since HTTP calls should never be used in a test, actions require extreme 137mocking. Mocking tests rely on assumptions and can lead to faulty tests. 138 139#### Getters 140 141Getters are JavaScript functions that return an output. These are basic 142functions that may not require testing unless there is getter logic. Any logic 143in a getter should be tested. 144 145#### Mutations 146 147Mutations are JavaScript functions that mutate the store state. These are basic 148functions that may not require testing unless there is mutation logic. Any logic 149in a mutation should be tested. 150 151#### Pros 152 153- Easier to debug 154- Smaller tests 155 156#### Cons 157 158- Requires extreme mocking when testing actions 159- Tightly coupled with implementation details 160- More maintenance required when refactoring 161 162### Testing Store Instance 163 164- Uses mutations and actions as inputs 165- State is the output 166- Requires the use of `localVue` when creating the store to avoid leaky tests 167 168#### Pros 169 170- Avoids mocking and brittle tests 171- Refactoring does not break test unless contract changes 172 173#### Cons 174 175- Debugging is more difficult 176 177## Vue Router 178 179- Our current structure does not warrant testing the vue router 180- If there is logic used for creating `RouteLink` items, we should unit test 181 that functionality, which requires stubbing 182- When testing a vue router, it is important to use localVue 183 184[Vuex Testing](https://vuex.vuejs.org/guide/testing.html) 185 186## Resources 187 188- [Vue Test Utils](https://vue-test-utils.vuejs.org/) 189- [Knowing What To Test — Vue Component Unit Testing](https://vuejsdevelopers.com/2019/08/26/vue-what-to-unit-test-components/) 190- [How to unit test a vuex Store](https://www.dev-tips-and-tricks.com/how-to-unit-test-a-vuex-store) 191