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)