An Introduction to TDD with Vue.js

A presentation at Vue.js Paris in June 2019 in Paris, France by Sarah Dayan

Slide 1

Slide 1

An Introduction to Test-Driven Development with Vue.js

Slide 2

Slide 2

Slide 3

Slide 3

Testing is one of the pillars of writing robust software.

Slide 4

Slide 4

Tests should give you confidence that you aren’t shipping broken software.

Slide 5

Slide 5

But… testing can be tough. Especially with UI components.

Slide 6

Slide 6

Slide 7

Slide 7

HTML? CSS classes? View logic? Event handlers? Methods? Computed properties? Lifecycle steps? 100% coverage? Unit vs. integration vs. e2e?

Slide 8

Slide 8

Artwork by Simon Ålander

Slide 9

Slide 9

Artwork by Simon Ålander

Slide 10

Slide 10

Test-Driven Development (TDD) Popularized by Kent Beck

Slide 11

Slide 11

1 Red Write a test that describes an expected behavior, then run it, ensuring it fails.

Slide 12

Slide 12

2 Green Write the dumbest, most straightforward code you can to make the test pass.

Slide 13

Slide 13

3 Refactor Refactor the code to make it right.

Slide 14

Slide 14

TDD

Slide 15

Slide 15

What does TDD look like with Vue?

Slide 16

Slide 16

Slide 17

Slide 17

src/components/Rating.vue 1 2 3 4 5 <template> </template> <script> </script>

Slide 18

Slide 18

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() })

Slide 19

Slide 19

Thinking time!

Slide 20

Slide 20

5 stars to display

Slide 21

Slide 21

Red

Slide 22

Slide 22

1 describe(‘Rating’, () => { 2 test(‘renders a list of stars’, () => { 3 const stars = wrapper.findAll(‘.star’) 4 expect(stars.length).toBe(5) 5 }) 6 })

Slide 23

Slide 23

Terminal npm run test:unit —watchAll

Slide 24

Slide 24

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) ^ })

Slide 25

Slide 25

Green

Slide 26

Slide 26

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>

Slide 27

Slide 27

PASS tests/unit/Rating.spec.js Rating ✓ renders a list of stars (21ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total

Slide 28

Slide 28

Refactor

Slide 29

Slide 29

1 2 beforeEach(() => { 3 wrapper = shallowMount(Rating, { 4 propsData: { 5 maxStars: 5 6 } 7 }) 8 })

Slide 30

Slide 30

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>

Slide 31

Slide 31

PASS tests/unit/Rating.spec.js Rating ✓ renders a list of stars (6ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total

Slide 32

Slide 32

Slide 33

Slide 33

The whole idea of TDD is not to write code to make things work, but to make tests pass. pass.

Slide 34

Slide 34

Thinking time!

Slide 35

Slide 35

2 active stars to display

Slide 36

Slide 36

Slide 37

Slide 37

1 test(‘renders active stars with class active’, () => { 2 const activeStars = wrapper.findAll(‘.active’) 3 expect(activeStars.length).toBe(2) 4 })

Slide 38

Slide 38

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 | })

Slide 39

Slide 39

1 <li 2 :key=”star” 3 v-for=”star in maxStars” 4 :class=”{ ‘active’: star <= 2 }” 5 class=”star”></li>

Slide 40

Slide 40

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

Slide 41

Slide 41

1 beforeEach(() => { 2 wrapper = shallowMount(Rating, { 3 propsData: { 4 maxStars: 5, 5 initialGrade: 2 6 } 7 }) 8 })

Slide 42

Slide 42

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>

Slide 43

Slide 43

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

Slide 44

Slide 44

What exactly are we testing?

Slide 45

Slide 45

Tests should give you confidence that you aren’t shipping broken software.

Slide 46

Slide 46

Black box testing Assert only the public interface.

Slide 47

Slide 47

Who are your users?

Slide 48

Slide 48

Final user

Slide 49

Slide 49

Developer

Slide 50

Slide 50

submit event click event props With UI components, your public interface is bigger than you think. HTML classes scroll event hover event

Slide 51

Slide 51

Do I care about this if it changes?

Slide 52

Slide 52

Slide 53

Slide 53

1 TDD is a fantastic way to write robust tests, tests, not too many and not too few.

Slide 54

Slide 54

2 TDD encourages refactors, which leads to better software design. design.

Slide 55

Slide 55

Slide 56

Slide 56

3 TDD is much easier to follow with specs. specs.

Slide 57

Slide 57

But… TDD takes a lot of time. time.

Slide 58

Slide 58

So, is it worth it?

Slide 59

Slide 59

1 With practice, you will get faster at TDD. TDD.

Slide 60

Slide 60

2 Fixing bugs is far more costly than preventing them. them.

Slide 61

Slide 61

Slide 62

Slide 62

Slide 63

Slide 63

Thank you! Questions? @frontstuff_io github.com/sarahdayan frontstuff.io/an-introduction-to-tdd-with-vuejs