Skip to content

Commit d65ee3f

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

File tree

7 files changed

+366
-0
lines changed

7 files changed

+366
-0
lines changed
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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+
```python
118+
def test_dog_runs(dog):
119+
with Dog("Pingo") as dog:
120+
dog.run()
121+
122+
assert dog.is_running()
123+
```
124+
125+
--
126+
127+
<!-- .slide: data-transition="fade-in slide-out" -->
128+
129+
```python
130+
@pytest.fixture
131+
def dog()
132+
with Dog("Pingo") as dog:
133+
yield dog
134+
135+
def test_dog_runs(dog):
136+
dog.run()
137+
138+
assert dog.is_running()
139+
```
140+
141+
--
142+
143+
# Isolation
144+
145+
Note:
146+
- No dependency between tests.
147+
- Be a good citizen, clean on exit.
148+
149+
Avoid leaks, it leads to chaos.
150+
151+
--
152+
153+
```golang
154+
var _ = Describe("Dog", func() {
155+
var dog = &Dog{}
156+
Context("is hungry", func() {
157+
var gulash := newGulash()
158+
159+
BeforeEach(func() { dog.MakeHungry() })
160+
161+
It("eats gulash and feels full", func() {
162+
dog.Eat(gulash)
163+
164+
Equal(dog.isHungry()).To(BeFalse())
165+
})
166+
})
167+
})
168+
169+
```
170+
171+
--
172+
```golang
173+
var _ = Describe("Dog", func() {
174+
var dog = &Dog{}
175+
Context("is hungry", func() {
176+
var gulash := newGulash()
177+
178+
BeforeEach(func() { dog.Reborn() })
179+
BeforeEach(func() { dog.MakeHungry() })
180+
181+
It("eats gulash and feels full", func() {
182+
dog.Eat(gulash)
183+
184+
Equal(dog.isHungry()).To(BeFalse())
185+
})
186+
})
187+
})
188+
189+
```
190+
191+
--
192+
193+
```golang
194+
var _ = Describe("Dog", func() {
195+
var dog = &Dog{}
196+
Context("is hungry", func() {
197+
var gulash := newGulash()
198+
199+
BeforeEach(func() { dog.MakeHungry() })
200+
AfterEach(func() { dog.Reborn() })
201+
202+
It("eats gulash and feels full", func() {
203+
dog.Eat(gulash)
204+
205+
Equal(dog.isHungry()).To(BeFalse())
206+
})
207+
})
208+
})
209+
210+
```
211+
212+
--
213+
214+
# Keep Assertions visible
215+
216+
--
217+
218+
# Traceability
219+
220+
--
221+
222+
# Shared Resources
223+
224+
<!-- .slide: data-background-image="share_keyboard.jpg" data-background-opacity="0.3"-->
225+
226+
--
227+
228+
# Continue On Failure
229+
230+
Note: Expect tests to fail, stopping on the first failure is not informative
231+
enough.
232+
Said that, support leaving the system "freezed" for advance debugging.
233+
234+
<!-- .slide: data-background-image="never_give_up.jpg" data-background-opacity="0.3"-->
235+
236+
--
237+
238+
# Avoid Dead Tests
239+
240+
--
241+
242+
# Parallel Tests
243+
244+
Note: Production code has tests. Tests have only themselves.
245+
Do not leave unexecuted code, it rots.
246+
Keep only running tests.
247+
248+
<!-- .slide: data-background-image="parallel.jpg" data-background-opacity="0.3"-->
249+
250+
--
251+
252+
# XFail
253+
254+
Expect to Fail
255+
256+
Note:
257+
Record a detected bug or a missing feature.
258+
259+
--
260+
261+
# !Randomization & Logic
262+
263+
Note:
264+
Random Testing have their own test category, do not mix them with
265+
"regular" tests.
266+
267+
Logic in tests should be limited, it competes with production code and
268+
may by itself be(come) wrong.
269+
270+
---
271+
272+
# The imperfect world
273+
274+
Workarounds
275+
276+
--
277+
278+
# Stop On Failure
279+
280+
--
281+
282+
# Clean @ Setup
283+
284+
---
285+
286+
# Thank You
287+
288+
https://ehaas.net/slides/decks/go_write_tests_using_pragmatic_best_practices
289+
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>
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)