Skip to content

Commit a1effe7

Browse files
committed
decks: Go write tests using using pragmatic best practices
Signed-off-by: Edward Haas <[email protected]>
1 parent b9beee6 commit a1effe7

File tree

7 files changed

+376
-0
lines changed

7 files changed

+376
-0
lines changed
347 KB
Loading
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
# Go Write Tests
2+
using Pragmatic Best Practices
3+
4+
Edward Haas, @redhat <!-- .element: style="position: absolute; left: 0; top: 100%; font-size: 0.6em" -->
5+
[email protected] <!-- .element: style="position: absolute; left: 0; top: 120%; font-size: 0.6em" -->
6+
7+
---
8+
9+
# /me
10+
11+
---
12+
13+
# Testing
14+
15+
Why?
16+
17+
Note: Because we are not prefect!
18+
19+
---
20+
21+
# Testing Levels
22+
23+
- Unit
24+
- Integration
25+
- System
26+
27+
Note:
28+
We can also consider compilation of typed languages and linters as an
29+
early development testing level.
30+
31+
Most practices should fit them all.
32+
33+
---
34+
35+
# Test Definitions
36+
37+
--
38+
39+
# Test Format <!-- .element: style="font-size: 1.8em" -->
40+
41+
- Setup
42+
- Exercise
43+
- Verify
44+
- Teardown
45+
46+
Note: A test is expected to include these steps in its flow, although
47+
some may have no content.
48+
49+
--
50+
51+
## Assertion
52+
53+
expected vs actual results
54+
55+
Note:
56+
- Appear mainly in the verify step, but may also end up in the setup and
57+
teardown.
58+
- Some test frameworks differentiate the error type when failing in the
59+
fixtures or in the test body (exercise and verify step).
60+
61+
--
62+
63+
# Skipping
64+
65+
- Explicitly by the test runner.
66+
- Explicitly by the test author.
67+
- Conditioned at run-time.
68+
69+
Note:
70+
It is useful to:
71+
- Run a subset of tests, filtering out some.
72+
- Declare in-code that a test is broken and needs attention (XFail).
73+
- Dynamically detect that a test cannot run on the platform.
74+
I consider this a bad practice, as it may be missed.
75+
76+
Consider the use of marking/labeling to filter the test list.
77+
This leaves the control to the test runner logic and avoids unintentional
78+
skips to occur (which in turn introduces holes in the coverage).
79+
80+
--
81+
82+
# Focus
83+
84+
Note:
85+
This is the opposite of skipping.
86+
87+
Run only a specific test group, which is useful during development.
88+
89+
It emphasises the need to have a test well isolated and not dependent on other
90+
tests.
91+
92+
---
93+
94+
# Test Best Practices
95+
96+
Note:
97+
Language and framework agnostic.
98+
99+
May be opinionated and controversial.
100+
101+
<!-- .slide: data-background-image="cat_glasses.jpg" data-background-opacity="0.4"-->
102+
103+
--
104+
105+
# Fail First
106+
107+
Note: Are you sure the test can fail? Maybe it always passes.
108+
109+
--
110+
111+
# Body vs Fixture
112+
113+
Note: Separate setup/teardown from test body
114+
115+
--
116+
117+
<!-- .slide: data-transition="fade" -->
118+
119+
```python
120+
def test_dog_runs(dog):
121+
with Dog("Pingo") as dog:
122+
dog.run()
123+
124+
assert dog.is_running()
125+
```
126+
127+
--
128+
129+
<!-- .slide: data-transition="fade" -->
130+
131+
```python
132+
@pytest.fixture
133+
def dog()
134+
with Dog("Pingo") as dog:
135+
yield dog
136+
137+
def test_dog_runs(dog):
138+
dog.run()
139+
140+
assert dog.is_running()
141+
```
142+
143+
--
144+
145+
# Isolation
146+
147+
Note:
148+
- No dependency between tests.
149+
- Be a good citizen, clean on exit.
150+
151+
Avoid leaks, it leads to chaos.
152+
153+
--
154+
155+
<!-- .slide: data-transition="fade" -->
156+
157+
```golang
158+
var _ = Describe("Dog", func() {
159+
var dog = &Dog{}
160+
Context("is hungry", func() {
161+
var gulash := newGulash()
162+
163+
164+
BeforeEach(func() { dog.MakeHungry() })
165+
166+
It("eats gulash and feels full", func() {
167+
dog.Eat(gulash)
168+
169+
Equal(dog.isHungry()).To(BeFalse())
170+
})
171+
})
172+
})
173+
174+
```
175+
176+
--
177+
178+
<!-- .slide: data-transition="fade" -->
179+
180+
```golang
181+
var _ = Describe("Dog", func() {
182+
var dog = &Dog{}
183+
Context("is hungry", func() {
184+
var gulash := newGulash()
185+
186+
BeforeEach(func() { dog.Reborn() })
187+
BeforeEach(func() { dog.MakeHungry() })
188+
189+
It("eats gulash and feels full", func() {
190+
dog.Eat(gulash)
191+
192+
Equal(dog.isHungry()).To(BeFalse())
193+
})
194+
})
195+
})
196+
197+
```
198+
199+
--
200+
201+
<!-- .slide: data-transition="fade" -->
202+
203+
```golang
204+
var _ = Describe("Dog", func() {
205+
var dog = &Dog{}
206+
Context("is hungry", func() {
207+
var gulash := newGulash()
208+
209+
BeforeEach(func() { dog.MakeHungry() })
210+
AfterEach(func() { dog.Reborn() })
211+
212+
It("eats gulash and feels full", func() {
213+
dog.Eat(gulash)
214+
215+
Equal(dog.isHungry()).To(BeFalse())
216+
})
217+
})
218+
})
219+
220+
```
221+
222+
--
223+
224+
# Keep Assertions visible
225+
226+
--
227+
228+
# Traceability
229+
230+
--
231+
232+
# Shared Resources
233+
234+
<!-- .slide: data-background-image="share_keyboard.jpg" data-background-opacity="0.3"-->
235+
236+
--
237+
238+
# Continue On Failure
239+
240+
Note: Expect tests to fail, stopping on the first failure is not informative
241+
enough.
242+
Said that, support leaving the system "freezed" for advance debugging.
243+
244+
<!-- .slide: data-background-image="never_give_up.jpg" data-background-opacity="0.3"-->
245+
246+
--
247+
248+
# Avoid Dead Tests
249+
250+
--
251+
252+
# Parallel Tests
253+
254+
Note: Production code has tests. Tests have only themselves.
255+
Do not leave unexecuted code, it rots.
256+
Keep only running tests.
257+
258+
<!-- .slide: data-background-image="parallel.jpg" data-background-opacity="0.3"-->
259+
260+
--
261+
262+
# XFail
263+
264+
Expect to Fail
265+
266+
Note:
267+
Record a detected bug or a missing feature.
268+
269+
--
270+
271+
# !Randomization & Logic
272+
273+
Note:
274+
Random Testing have their own test category, do not mix them with
275+
"regular" tests.
276+
277+
Logic in tests should be limited, it competes with production code and
278+
may by itself be(come) wrong.
279+
280+
---
281+
282+
# The imperfect world
283+
284+
Workarounds
285+
286+
--
287+
288+
# Stop On Failure
289+
290+
--
291+
292+
# Clean @ Setup
293+
294+
---
295+
296+
# Thank You
297+
298+
https://ehaas.net/slides/decks/go_write_tests_using_pragmatic_best_practices
299+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6+
7+
<title>Go Write Tests using Pragmatic Best Practices</title>
8+
<meta name="description" content="Tests are written to assure the correctness and quality of the solution they examine.
9+
Engineers write tests at different stages of the development cycle, starting at unit tests up to e2e tests.
10+
In fact, for every line of production code, multiple lines of test code is written.
11+
12+
Writing tests is no different from writing production code. In order to keep it running correctly and assist in detecting issues, it needs to be written in a way that can stand the test of time, provide the needed information on failures and be maintained for the project lifetime.
13+
14+
This talk presents test best practices which can be applied at all test stages.
15+
The practices are focused to help write good tests and provide the needed information for debugging and troubleshooting the issues detected.
16+
17+
While each language and test framework may present different properties and challenges, the practices are agnostic to a specific language/tool.
18+
19+
Examples will be given in Golang using Ginkgo/Gomega and in Python using PyTest.
20+
21+
The talk will cover:
22+
- Test structure
23+
- Test isolation
24+
- Test fixtures vs test body
25+
- Assertion
26+
- Traceability
27+
- Shared resources
28+
- Dead test (code)
29+
- Skipping, xfailing or not running
30+
- Parallel tests
31+
32+
The talk is based on the following blog post: https://ehaas.net/blog/tests-best-practices">
33+
34+
<link rel="stylesheet" href="../../css/reset.css">
35+
<link rel="stylesheet" href="../../css/reveal.css">
36+
<link rel="stylesheet" href="../../css/theme/black.css">
37+
38+
<!-- Theme used for syntax highlighting of code -->
39+
<link rel="stylesheet" href="../../lib/css/monokai.css">
40+
41+
<!-- Printing and PDF exports -->
42+
<script>
43+
var link = document.createElement( 'link' );
44+
link.rel = 'stylesheet';
45+
link.type = 'text/css';
46+
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
47+
document.getElementsByTagName( 'head' )[0].appendChild( link );
48+
</script>
49+
</head>
50+
<body>
51+
<div class="reveal">
52+
<div class="slides">
53+
<section data-markdown="content.md"
54+
data-separator-vertical="^\r?\n--\r?\n$"
55+
data-separator-notes="^note:">
56+
</section>
57+
</div>
58+
</div>
59+
60+
<script src="../../js/reveal.js"></script>
61+
62+
<script>
63+
// More info about config & dependencies:
64+
// - https://github.com/hakimel/reveal.js#configuration
65+
// - https://github.com/hakimel/reveal.js#dependencies
66+
Reveal.initialize({
67+
dependencies: [
68+
{ src: '../../plugin/markdown/marked.js' },
69+
{ src: '../../plugin/markdown/markdown.js' },
70+
{ src: '../../plugin/notes/notes.js', async: true },
71+
{ src: '../../plugin/highlight/highlight.js', async: true },
72+
{ src: '../../plugin/zoom-js/zoom.js', async: true }
73+
]
74+
});
75+
</script>
76+
</body>
77+
</html>
335 KB
Loading
106 KB
Loading
141 KB
Loading
85.6 KB
Loading

0 commit comments

Comments
 (0)