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