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