1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Unit Testing | OpenBMC Web UI Style Guide</title>
<meta name="generator" content="VuePress 1.8.2">
<meta name="description" content="Guidance on code style and development for the OpenBMC browser-based UI">
<link rel="preload" href="/webui-vue/assets/css/0.styles.ee48119c.css" as="style"><link rel="preload" href="/webui-vue/assets/js/app.6e5ae7e9.js" as="script"><link rel="preload" href="/webui-vue/assets/js/2.3066748c.js" as="script"><link rel="preload" href="/webui-vue/assets/js/37.c48db488.js" as="script"><link rel="prefetch" href="/webui-vue/assets/js/10.eff1bc3e.js"><link rel="prefetch" href="/webui-vue/assets/js/11.f760a167.js"><link rel="prefetch" href="/webui-vue/assets/js/12.05e32ea2.js"><link rel="prefetch" href="/webui-vue/assets/js/13.3011b8ba.js"><link rel="prefetch" href="/webui-vue/assets/js/14.366e135a.js"><link rel="prefetch" href="/webui-vue/assets/js/15.9a576a5a.js"><link rel="prefetch" href="/webui-vue/assets/js/16.8d8a2baa.js"><link rel="prefetch" href="/webui-vue/assets/js/17.067a94f4.js"><link rel="prefetch" href="/webui-vue/assets/js/18.88267105.js"><link rel="prefetch" href="/webui-vue/assets/js/19.781066f3.js"><link rel="prefetch" href="/webui-vue/assets/js/20.afaf92e0.js"><link rel="prefetch" href="/webui-vue/assets/js/21.f51dd518.js"><link rel="prefetch" href="/webui-vue/assets/js/22.22ba3b96.js"><link rel="prefetch" href="/webui-vue/assets/js/23.42b49486.js"><link rel="prefetch" href="/webui-vue/assets/js/24.7c316dcb.js"><link rel="prefetch" href="/webui-vue/assets/js/25.79d3357c.js"><link rel="prefetch" href="/webui-vue/assets/js/26.c82c48a9.js"><link rel="prefetch" href="/webui-vue/assets/js/27.f4d5e099.js"><link rel="prefetch" href="/webui-vue/assets/js/28.e8d46e0f.js"><link rel="prefetch" href="/webui-vue/assets/js/29.a85a251f.js"><link rel="prefetch" href="/webui-vue/assets/js/3.4aa24478.js"><link rel="prefetch" href="/webui-vue/assets/js/30.994c9683.js"><link rel="prefetch" href="/webui-vue/assets/js/31.06af27be.js"><link rel="prefetch" href="/webui-vue/assets/js/32.57a17f1c.js"><link rel="prefetch" href="/webui-vue/assets/js/33.b9e5db10.js"><link rel="prefetch" href="/webui-vue/assets/js/34.0e496acd.js"><link rel="prefetch" href="/webui-vue/assets/js/35.49b0ff49.js"><link rel="prefetch" href="/webui-vue/assets/js/36.dbed5155.js"><link rel="prefetch" href="/webui-vue/assets/js/38.2cecf29e.js"><link rel="prefetch" href="/webui-vue/assets/js/4.e088be67.js"><link rel="prefetch" href="/webui-vue/assets/js/5.666cf43b.js"><link rel="prefetch" href="/webui-vue/assets/js/6.c4bd7641.js"><link rel="prefetch" href="/webui-vue/assets/js/7.2afc0a47.js"><link rel="prefetch" href="/webui-vue/assets/js/8.50aab72d.js"><link rel="prefetch" href="/webui-vue/assets/js/9.9f3d8ba6.js">
<link rel="stylesheet" href="/webui-vue/assets/css/0.styles.ee48119c.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/webui-vue/" class="home-link router-link-active"><!----> <span class="site-name">OpenBMC Web UI Style Guide</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/webui-vue/guide/" class="nav-link router-link-active">
Guide
</a></div><div class="nav-item"><a href="/webui-vue/customization/" class="nav-link">
Customization
</a></div><div class="nav-item"><a href="https://github.com/openbmc/webui-vue" target="_blank" rel="noopener noreferrer" class="nav-link external">
Github
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/webui-vue/guide/" class="nav-link router-link-active">
Guide
</a></div><div class="nav-item"><a href="/webui-vue/customization/" class="nav-link">
Customization
</a></div><div class="nav-item"><a href="https://github.com/openbmc/webui-vue" target="_blank" rel="noopener noreferrer" class="nav-link external">
Github
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <!----></nav> <ul class="sidebar-links"><li><a href="/webui-vue/guide/" aria-current="page" class="sidebar-link">Getting Started</a></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Coding Standards</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Guidelines</span> <span class="arrow right"></span></p> <!----></section></li><li><a href="/webui-vue/guide/unit-testing/" aria-current="page" class="active sidebar-link">Unit Testing</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#test-libraries" class="sidebar-link">Test Libraries</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#test-specification-location-and-naming-conventions" class="sidebar-link">Test specification location and naming conventions</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#running-tests" class="sidebar-link">Running Tests</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#guidelines" class="sidebar-link">Guidelines</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#components" class="sidebar-link">Components</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#vuex-store" class="sidebar-link">Vuex Store</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#vue-router" class="sidebar-link">Vue Router</a></li><li class="sidebar-sub-header"><a href="/webui-vue/guide/unit-testing/#resources" class="sidebar-link">Resources</a></li></ul></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Components</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Quick Start</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="unit-testing"><a href="#unit-testing" class="header-anchor">#</a> Unit Testing</h1> <p>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.</p> <blockquote><p>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.</p> <p>-- <cite>Ed Yerburgh, Testing Vue Applications (New York: Manning
Publications, 2019)</cite></p></blockquote> <h2 id="test-libraries"><a href="#test-libraries" class="header-anchor">#</a> Test Libraries</h2> <p>The OpenBMC Web UI unit test framework uses the Jest test runner and relies on
the following libraries:</p> <ul><li>@vue/cli-plugin-unit-jest</li> <li>@vue/test-utils</li></ul> <h2 id="test-specification-location-and-naming-conventions"><a href="#test-specification-location-and-naming-conventions" class="header-anchor">#</a> Test specification location and naming conventions</h2> <ul><li>Create the test files in the /tests/unit directory</li> <li>The naming convention is to replicate the folder and component name</li></ul> <h3 id="examples"><a href="#examples" class="header-anchor">#</a> Examples</h3> <ul><li>The AppHeader.vue single-file component's (SFC) spec file is named
<code>AppHeader.spec.js</code></li> <li>Create a global component like <code>PageSection.vue</code> in the <code>/tests/global</code>
directory with the name <code>PageSection.spec.js</code></li> <li>Create a mixin like BVToastMixin in the <code>/tests/mixins</code> directory with the
name <code>BVToastMixin.spec.js</code> Running Tests</li></ul> <h2 id="running-tests"><a href="#running-tests" class="header-anchor">#</a> Running Tests</h2> <p>The <code>test:unit</code> 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.</p> <h3 id="debugging-a-failed-test"><a href="#debugging-a-failed-test" class="header-anchor">#</a> Debugging a failed test</h3> <p>The <code>test:unit:debugger</code> script will help to debug failing tests using the
Chrome Developer Tools. To debug a test:</p> <ol><li>Add a <code>debugger</code> statement in the specifications file</li> <li>Run the unit test in debugger mode</li> <li>Open the Chrome browser and go to <code>chrome://inspect</code></li></ol> <h3 id="fixing-failed-snapshot-tests"><a href="#fixing-failed-snapshot-tests" class="header-anchor">#</a> Fixing failed snapshot tests</h3> <p>The <code>test:update</code> 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 <code>test:update</code> can be dangerous,
as it will update all snapshot tests.</p> <p>It is critical to verify all snapshot tests before running the update script.
The easiest way is to run the unit test in watch mode, <code>npm run test:unit -- --watch</code> and verify each snapshot.</p> <h2 id="guidelines"><a href="#guidelines" class="header-anchor">#</a> Guidelines</h2> <ul><li>Avoid coupling test code to source code when testing functionality
<ul><li>If test cases fail during refactoring, the test case may be tightly
coupled with the application structure.</li></ul></li> <li>A test should not break if the functionality it tests has not changed</li> <li>To maintain test readability, only pass in the data needed for the test to
work in your mock object</li> <li>Avoid the creation of side-effects whenever possible</li> <li>There is no return on investment for testing presentational HTML</li> <li>Use <code>shallowMount</code> rather than mount unless child component rendering is
required</li> <li>Avoid leaky tests by using <code>localVue</code> for all plugin installs, for example,
when testing a plugin like Vuex</li></ul> <h2 id="components"><a href="#components" class="header-anchor">#</a> Components</h2> <h3 id="what-to-test"><a href="#what-to-test" class="header-anchor">#</a> What to test</h3> <ol><li>Test the function's inputs and outputs
<ul><li>Test only dynamically generated output</li> <li>Test only output that is part of the component contract</li></ul></li> <li>Test any side-effects</li> <li>Test correct rendering using a snapshot test</li></ol> <h3 id="what-not-to-test"><a href="#what-not-to-test" class="header-anchor">#</a> What not to test</h3> <ol><li>Don't test third-party functionality</li> <li>Don't test the internals of your components or that specific functions are
called. This can lead to unnecessary refactoring.</li> <li>Don't go beyond the input and outputs of the component</li> <li>Don't test the functionality of other libraries</li> <li>Static components do not need unit tests, use snapshot testing</li></ol> <h3 id="strategy"><a href="#strategy" class="header-anchor">#</a> Strategy</h3> <ol><li>Define a component contract that is based upon the component API</li> <li>Create smaller functions with a specific purpose to make testing easier</li> <li>Test the component API by writing tests first and then writing code to fix
the tests</li> <li>Add a snapshot test once the presentational layer is validated through manual
visual testing</li></ol> <h3 id="snapshot-testing"><a href="#snapshot-testing" class="header-anchor">#</a> Snapshot Testing</h3> <p>A 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.</p> <p>This 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.</p> <h2 id="vuex-store"><a href="#vuex-store" class="header-anchor">#</a> Vuex Store</h2> <p>There 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 <code>test store parts separately</code>.</p> <h3 id="testing-store-parts-separately"><a href="#testing-store-parts-separately" class="header-anchor">#</a> Testing Store Parts Separately</h3> <p>Testing the parts separately is easy since each of the parts is a JavaScript
function. Store parts to test include <code>actions</code>, <code>getters</code>, and <code>mutations</code>.</p> <h4 id="actions"><a href="#actions" class="header-anchor">#</a> Actions</h4> <p>Since HTTP calls should never be used in a test, actions require extreme
mocking. Mocking tests rely on assumptions and can lead to faulty tests.</p> <h4 id="getters"><a href="#getters" class="header-anchor">#</a> Getters</h4> <p>Getters 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.</p> <h4 id="mutations"><a href="#mutations" class="header-anchor">#</a> Mutations</h4> <p>Mutations 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.</p> <h4 id="pros"><a href="#pros" class="header-anchor">#</a> Pros</h4> <ul><li>Easier to debug</li> <li>Smaller tests</li></ul> <h4 id="cons"><a href="#cons" class="header-anchor">#</a> Cons</h4> <ul><li>Requires extreme mocking when testing actions</li> <li>Tightly coupled with implementation details</li> <li>More maintenance required when refactoring</li></ul> <h3 id="testing-store-instance"><a href="#testing-store-instance" class="header-anchor">#</a> Testing Store Instance</h3> <ul><li>Uses mutations and actions as inputs</li> <li>State is the output</li> <li>Requires the use of <code>localVue</code> when creating the store to avoid leaky tests</li></ul> <h4 id="pros-2"><a href="#pros-2" class="header-anchor">#</a> Pros</h4> <ul><li>Avoids mocking and brittle tests</li> <li>Refactoring does not break test unless contract changes</li></ul> <h4 id="cons-2"><a href="#cons-2" class="header-anchor">#</a> Cons</h4> <ul><li>Debugging is more difficult</li></ul> <h2 id="vue-router"><a href="#vue-router" class="header-anchor">#</a> Vue Router</h2> <ul><li>Our current structure does not warrant testing the vue router</li> <li>If there is logic used for creating <code>RouteLink</code> items, we should unit test
that functionality, which requires stubbing</li> <li>When testing a vue router, it is important to use localVue</li></ul> <p><a href="https://vuex.vuejs.org/guide/testing.html" target="_blank" rel="noopener noreferrer">Vuex Testing<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <h2 id="resources"><a href="#resources" class="header-anchor">#</a> Resources</h2> <ul><li><a href="https://vue-test-utils.vuejs.org/" target="_blank" rel="noopener noreferrer">Vue Test Utils<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://vuejsdevelopers.com/2019/08/26/vue-what-to-unit-test-components/" target="_blank" rel="noopener noreferrer">Knowing What To Test — Vue Component Unit
Testing<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://www.dev-tips-and-tricks.com/how-to-unit-test-a-vuex-store" target="_blank" rel="noopener noreferrer">How to unit test a vuex
Store<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
←
<a href="/webui-vue/guide/guidelines/typography.html" class="prev">
Typography
</a></span> <span class="next"><a href="/webui-vue/guide/components/">
Overview
</a>
→
</span></p></div> </main></div><div class="global-ui"></div></div>
<script src="/webui-vue/assets/js/app.6e5ae7e9.js" defer></script><script src="/webui-vue/assets/js/2.3066748c.js" defer></script><script src="/webui-vue/assets/js/37.c48db488.js" defer></script>
</body>
</html>
|