A presentation at Vue.js Paris in June 2019 in Paris, France by Sarah Dayan
An Introduction to Test-Driven Development with Vue.js
Testing is one of the pillars of writing robust software.
Tests should give you confidence that you aren’t shipping broken software.
But… testing can be tough. Especially with UI components.
HTML? CSS classes? View logic? Event handlers? Methods? Computed properties? Lifecycle steps? 100% coverage? Unit vs. integration vs. e2e?
Artwork by Simon Ålander
Test-Driven Development (TDD) Popularized by Kent Beck
1 Red Write a test that describes an expected behavior, then run it, ensuring it fails.
2 Green Write the dumbest, most straightforward code you can to make the test pass.
3 Refactor Refactor the code to make it right.
TDD
What does TDD look like with Vue?
src/components/Rating.vue 1 2 3 4 5 <template> </template> <script> </script>
tests/unit/Rating.spec.js 1 2 3 4 5 6 7 8 9 10 11 12 13 import { shallowMount } from ‘@vue/test-utils’ import Rating from ‘@/components/Rating’ let wrapper = null beforeEach(() => { wrapper = shallowMount(Rating) }) afterEach(() => { wrapper.destroy() })
Thinking time!
5 stars to display
Red
1 describe(‘Rating’, () => { 2 test(‘renders a list of stars’, () => { 3 const stars = wrapper.findAll(‘.star’) 4 expect(stars.length).toBe(5) 5 }) 6 })
Terminal npm run test:unit —watchAll
FAIL tests/unit/Rating.spec.js Rating ✕ renders a list of stars (7ms) ● Rating › renders a list of stars expect(received).toBe(expected) // Object.is equality Expected: 5 Received: 0 16 | 17 | > 18 | | 19 | test(‘renders a list of stars’, () => { const stars = wrapper.findAll(‘.star’) expect(stars.length).toBe(5) ^ })
Green
1 <template> 2 <ul> 3 <li class=”star”></li> 4 <li class=”star”></li> 5 <li class=”star”></li> 6 <li class=”star”></li> 7 <li class=”star”></li> 8 </ul> 9 </template>
PASS tests/unit/Rating.spec.js Rating ✓ renders a list of stars (21ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total
Refactor
1 2 beforeEach(() => { 3 wrapper = shallowMount(Rating, { 4 propsData: { 5 maxStars: 5 6 } 7 }) 8 })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template> <ul> <li :key=”star” v-for=”star in maxStars” class=”star”></li> </ul> </template> <script> export default { props: { maxStars: { type: Number, default: 5 } } } </script>
PASS tests/unit/Rating.spec.js Rating ✓ renders a list of stars (6ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total
The whole idea of TDD is not to write code to make things work, but to make tests pass. pass.
2 active stars to display
1 test(‘renders active stars with class active’, () => { 2 const activeStars = wrapper.findAll(‘.active’) 3 expect(activeStars.length).toBe(2) 4 })
active
FAIL tests/unit/Rating.spec.js Rating ✓ renders a list of stars (5ms) ✕ renders active stars with class active (9ms) ● Rating › renders active stars with class active expect(received).toBe(expected) // Object.is equality Expected: 2 Received: 0 24 | test(‘renders active stars with class active’, () => { 25 | const activeStars = wrapper.findAll(‘.active’) > 26 | expect(activeStars.length).toBe(2) | ^ 27 | }) 28 | })
1 <li 2 :key=”star” 3 v-for=”star in maxStars” 4 :class=”{ ‘active’: star <= 2 }” 5 class=”star”></li>
PASS tests/unit/Rating.spec.js Rating ✓ renders a list of stars (6ms) ✓ renders active stars with class active (4ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total
1 beforeEach(() => { 2 wrapper = shallowMount(Rating, { 3 propsData: { 4 maxStars: 5, 5 initialGrade: 2 6 } 7 }) 8 })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <template> <!— … —> <li :key=”star” v-for=”star in maxStars” :class=”{ ‘active’: star <= initialGrade }” class=”star”></li> <!— … —> </template> <script> export default { props: { // … initialGrade: { type: Number, default: 0 } } } </script>
PASS tests/unit/Rating.spec.js Rating ✓ renders a list of stars (4ms) ✓ renders active stars with class active (2ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total
What exactly are we testing?
Black box testing Assert only the public interface.
Who are your users?
Final user
Developer
submit event click event props With UI components, your public interface is bigger than you think. HTML classes scroll event hover event
Do I care about this if it changes?
1 TDD is a fantastic way to write robust tests, tests, not too many and not too few.
2 TDD encourages refactors, which leads to better software design. design.
3 TDD is much easier to follow with specs. specs.
But… TDD takes a lot of time. time.
So, is it worth it?
1 With practice, you will get faster at TDD. TDD.
2 Fixing bugs is far more costly than preventing them. them.
Thank you! Questions? @frontstuff_io github.com/sarahdayan frontstuff.io/an-introduction-to-tdd-with-vuejs
View An Introduction to TDD with Vue.js on Notist.
Dismiss
The following resources were mentioned during the presentation or are useful additional information.
Here’s what was said about this presentation on social media.
@frontstuff_io starting the #vuejsparis meetup to talk about TDD and components 🔬 pic.twitter.com/g8t4ax8Mms— Vue.js Paris (@vuejsparis) June 5, 2019
@frontstuff_io starting the #vuejsparis meetup to talk about TDD and components 🔬 pic.twitter.com/g8t4ax8Mms