@@ -44,77 +44,68 @@ to get a call at 3:00 AM to fix a bug that has crashed my app!
44
44
45
45
## Guiding principles for writing tests
46
46
47
+ The principles listed in this section are based on an article by Kent C. Dodds
48
+ titled
49
+ [ Write tests. Not too many. Mostly integration.] ( https://kentcdodds.com/blog/write-tests ) .
50
+ Kent is a testing guru with very good guidance on how to test. I have listed
51
+ several more of his useful articles in the references below. So without further
52
+ ado, let's jump in.
53
+
47
54
### Don't test implementation details
48
55
49
- If your test does something that the consumer of your code doesn't, chances are
50
- that you are testing implementation details. For example, you may be exposing a
51
- private function just to test your component. This is a code smell, don't do
52
- it - a refactor can easily break your tests. Steer away from testing tools that
53
- allow you to test implementation details (e.g. Enzyme). Instead, use tools such
54
- as React Testing Library which make it very difficult to include implementation
55
- details in your tests. For more details, refer to
56
- [ Write tests. Not too many. Mostly integration.] ( https://kentcdodds.com/blog/write-tests )
57
- by Kent C. Dodds.
56
+ If your test does something that your user doesn't, chances are that you are
57
+ testing implementation details. For example, you may be exposing a private
58
+ function just to test your component. This is a code smell, don't do it - a
59
+ refactor can easily break your test. Steer away from testing tools that allow
60
+ you to test implementation details (e.g. Enzyme). Instead, use tools such as
61
+ React Testing Library that make it difficult to include implementation details
62
+ in your tests. For more details, refer to
58
63
59
64
### Test your components as a user would
60
65
61
- The traditional testing wisdom was to write a lot of unit tests to test
62
- individual "units". We used to isolate our components from their environment
63
- using mocks. This approach still makes sense to test pure functions where the
64
- output is strictly based on its input. However, for front-end components which
65
- depend on communicating with their surrounding components, mocking reduces our
66
- confidence in the integrations. The latest thinking is to test several units
67
- together to recreate real interaction scenarios, hence the name _ integration
68
- testing_ .
66
+ The classic testing wisdom was to write a lot of unit tests to test individual
67
+ "units" of code. We used to isolate our components from their environment using
68
+ mocks. This approach still makes sense for pure functions, but for front-end
69
+ components, which depend on communications with surrounding components, mocking
70
+ reduces our confidence in their integrations. So the latest thinking is to test
71
+ several units together to recreate real interaction scenarios, hence the name
72
+ _ integration testing_ .
69
73
70
74
This brings us to the guiding principle which is the foundation of the React
71
75
Testing Library:
72
76
73
77
> The more your tests resemble the way your software is used, the more
74
78
> confidence they can give you.
75
79
76
- This translates to writing more "integration" style tests where, for example,
77
- you may want to drop a couple of components under a ` <Context.Provider> ` to test
78
- real user interactions. Or you may use [ Mock Service Worker] ( https://mswjs.io/ )
79
- to mock APIs at the network level rather than mocking at the component or
80
+ This translates to writing more _ integration _ style tests where, for example,
81
+ you drop a couple of components under a ` <Context.Provider> ` to test real user
82
+ interactions. Or you may use [ Mock Service Worker] ( https://mswjs.io/ ) to mock
83
+ APIs at the network level rather than excessively mocking at the component or
80
84
service layer.
81
85
82
86
### Don't be obsessed with code coverage
83
87
84
88
There is a tradeoff between time spent writing tests and code coverage. Some
85
- organizations put undue focus on code coverage. Unfortunately this set the wrong
86
- goal for developers because after a certain point, the returns are not worth the
89
+ organizations put undue focus on code coverage. Unfortunately this sets the
90
+ wrong goal for developers - after a certain point, the returns are not worth the
87
91
effort. You start seeing developers gaming the system by writing meaningless
88
- tests. Instead, the focus should be on _ use case coverage_ . Think of all the use
89
- cases (including corner cases) that you want to test to feel confident about
90
- your code. This approach will automatically yield high code coverage.
92
+ tests. Instead, focus on _ use case coverage_ . Think of all the use cases
93
+ (including corner cases) that you want to test to feel confident about your
94
+ code. This approach will automatically yield high code coverage.
91
95
92
- ### Push business logic into pure functions vs. UI components
96
+ ### Push business logic into pure functions instead of UI components
93
97
94
98
For example, a Shopping Cart UI component should not compute the cart total.
95
99
This should be pushed to a
96
100
[ pure function] ( https://en.wikipedia.org/wiki/Pure_function ) because it is
97
- easier to test. See [ here] ( ./src/models/Cart.ts ) for examples for pure functions
98
- and [ related tests] ( ./src/models/Cart.test.ts ) .
99
-
100
- ### Snapshot testing vs. traditional unit testing
101
-
102
- Use snapshot tests only for small focused components where the snapshots can be
103
- easily read and verified for correctness (usually 20-30 lines max). For larger
104
- snapshots, developers tend to be undisciplined about reviewing them before
105
- committing, resulting in buggy code to be committed. Moreover, good tests encode
106
- the developer's intention. Snapshot tests lack the expression of this intent. So
107
- for anything beyond the simplest of components, prefer a traditional unit test.
108
-
109
- Note: If you need to write a snapshot test, I recommend using
110
- react-testing-library because it generates cleaner snapshots. The other popular
111
- way of generating snapshots is react-test-renderer, but its output contains
112
- component properties and other details that are not relevant. Here's an
113
- [ example of a snapshot test] ( ./src/pages/NotFoundPage/NotFoundPage.test.tsx )
114
- using react-testing-library.
101
+ easier to test. Even better, push it off to the back-end where more
102
+ sophisticated calculations can be performed without affecting the UI. See
103
+ [ here] ( ./src/models/Cart.ts ) for examples for pure functions and
104
+ [ related tests] ( ./src/models/Cart.test.ts ) .
115
105
116
106
## Techniques
117
107
108
+ - [ Snapshot testing vs. traditional unit testing] ( ./docs/snapshot-testing-vs-traditional-unit-testing.md )
118
109
- [ Difference between queryBy, getBy and findBy queries] ( ./docs/difference-between-query-types.md )
119
110
- Checking for existence of an element
120
111
- Waiting for removal of an element
0 commit comments