# Install dependencies
yarn install
# Run unit tests
yarn unit
# Run a specific unit test
yarn unit src/path/to/unit/test.unit.js
# Run a specific unit test, recording requests for playback
yarn unit:record src/path/to/unit/test.unit.js
codepilot
|- src # Unit tests live next to the file they test, with the .unit.js extension
|- test
|- unit
|- __mocks__/ # NPM module mocks
|- matchers.js # Additional matchers custom to our app
|- setup.js # Setup to run before each test file
It's when we test a single module/file, outside the context of our app. If every other file in the app were deleted, the unit tests for this module should still work.
It's definitely OK to refactor code to be more unit-testable. You can split a function into many smaller functions, or move an expression from a Vue template into a computed property. If there's anything you're not sure about, get a second opinion.
We use Jest to run our unit tests. If you're not familiar with it, I recommend first browsing through the existing tests to get a sense for them.
Then at the very least, read about:
- Jest's matchers for examples of other assertions you can make
- Testing async code
- Setup and teardown
If you're testing a Vue component, it's recommended to use Vue test utils.
See an example in test/unit/app-button.spec.js
. The shallow
function renders the Vue component, but not any child components. The c
variable is short for component
. On it, you most notably have access to:
vnode
: The virtual DOM node rendered by the component. See its type definition for details.element
: The root DOM element rendered by the component.vm
: The component instance.
You also have access to a number of methods, described in these docs.
To learn more, browse the in-progress docs for Vue's test utils.
Next to any source file, create a new file of the same name, but with the .unit.js
extension.
For all of our tests, we use the BDD syntax, where you describe the thing you want to test and the behavior it should have.
In the newly created .unit.js
file, add a describe
block that describes the thing you want to test, using the alias reference beginning with @
as the name of the test:
import AppIcon from './app-icon'
describe('@components/app-icon', function () {
// Add test cases for behavior(s) the AppIcon should have
})
Then, inside the describe
block, add as many test cases needed using the following syntax:
it('renders a span element with search icon', function () {
// Add some assertions, making sure to avoid boolean expectations such as:
// expect(foo).toEqual('bar')
})
import AppIcon from './app-icon'
describe('@components/app-icon', () => {
it('renders a span element with search icon', () => {
const { element } = mount(AppIcon, {
propsData: {
icon: 'search'
}
})
expect(element.tagName).toEqual('SPAN')
expect(element.className).toContain('fa fa-search')
})
})
We can record requests for a test with:
# Run a specific unit test, recording requests for playback
yarn unit:record src/path/to/unit/test.unit.js
This saves the request responses to the tests/unit/playbacks
directory. In future tests run with yarn unit
, these playbacks will be used instead of hitting the APIs directly. This avoids random test failures from hitting API limits and also significantly speeds up tests.