-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathunit_test.rst
427 lines (325 loc) · 16.3 KB
/
unit_test.rst
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
Let's write a unit test!
========================
| A unit test is a piece of code which tests one thing works well in
isolation from other parts of software. In this guide, I'm going to
explain how to write one using the standard python unittest module,
for the `pygame <https://www.pygame.org/>`__ game library. You can
apply this advice to most python projects, or free/libre open source
projects in general.
A minimal test
--------------
| **What pygame.draw.ellipse should do**:
http://www.pygame.org/docs/ref/draw.html#pygame.draw.ellipse
| **Where to put the test**:
https://github.com/pygame/pygame/blob/master/test/draw_test.py
|
::
def test_ellipse(self):
import pygame.draw
surf = pygame.Surface((320, 200))
pygame.draw.ellipse(surf, (255, 0, 0), (10, 10, 25, 20))
|
| All the test does is call the draw function on the surface with a
color, and a rectangle. That's it. A minimal, useful test. If you have
a github account, you can even edit the test file in the browser to
submit your PR. If you have email, or internet access you can email me
or someone else on the internet and ask them to do add it to pygame.
| An easy test to write... but it provides really good value.
- Shows an example of using the code.
- Makes sure the function arguments are correct.
- Makes sure the code runs on 20+ different platforms and python
versions.
- No "regressions" (Code that starts failing because of a change) can
be introduced in the future. The code for draw ellipse with these
arguments should not crash in the future.
|
But why write a unit test anyway?
---------------------------------
| Unit tests help pygame make sure things don't break on multiple
platforms. When your code is running on dozens of CPUs and just as
many operating systems things get a little tricky to test manually. So
we write a unit test and let all the build robots do that work for us.
| A great way to contribute to libre/free and open source projects is to
contribute a test. Less bugs in the library means less bugs in your
own code. Additionally, you get some public credit for your
contribution.
| The best part about it, is that it's a great way to learn python, and
about the thing you are testing. Want to know how graphics algorithms
should work, in lots of detail? Start writing tests for them.
The simplest test is to just call the function. Just calling it is a
great first test. Easy, and useful.
|
| At the time of writing there are 39 functions that aren't even called
when running the pygame tests. Why not join me on this adventure?
Let's write a unit test!
------------------------
| In this guide I'm going to write a test for an pygame.draw.ellipse to
make sure a thick circle has the correct colors in it, and not lots of
black spots. There's a bunch of tips and tricks to help you along your
way. Whilst you can just edit a test in your web browser, and submit a
PR, it might be more comfortable to do it in your normal development
environment.
Grab a fork, and let's dig in.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| `Set up git for
github <https://help.github.com/articles/set-up-git/>`__ if you
haven't already. Then you'll want to 'fork' pygame on
https://github.com/pygame/pygame so you have your own local copy.
.. container::
*Note, we also accept patches by email, or on github issues. So
you can*\ **skip all this github business**\ *if you want
to.*\ https://www.pygame.org/wiki/patchesandbugs
- Fork the repository (see top right of the `pygame repo
page <https://github.com/pygame/pygame>`__)
.. container:: separator
|image0|
- Make the change locally. Push to your copy of the fork.
- Submit a *pull request*
| So you've forked the repo, and now you can clone your own copy of the
git repo locally.
::
$ git clone https://github.com/YOUR-USERNAME/pygame
$ cd pygame/
$ python test/draw_test.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.007s
OK
|
| You'll see all of the tests in the test/ folder.
| Browse the test folder online:
https://github.com/pygame/pygame/tree/master/test
| |image1|
| If you have an older version of pygame, you can use this little
program to see the issue.
| There is some more extensive documentation in the test/README file.
Including on how to write a test that requires manual interaction.
Standard unittest module.
-------------------------
| pygame uses the standard python unittest module. With a few
enhancements to make it nicer for developing C code.
*Fun fact: pygame included the unit testing module before python
did.*
| We will go over the basics in this guide, but for more detailed
information please see:
| https://docs.python.org/3/library/unittest.html
How to run a single test?
-------------------------
| Running all the tests at once can take a while. What if you just want
to run a single test?
| If we look inside draw_test.py, each test is a class name, and a
function. There is a "DrawModuleTest" class, and there should be a
"def test_ellipse" function.
So, let's run the test...
~~~~~~~~~~~~~~~~~~~~~~~~~
::
~/pygame/ $ python test/draw_test.py DrawModuleTest.test_ellipse
Traceback (most recent call last):
...
AttributeError: type object 'DrawModuleTest' has no attribute 'test_ellipse'
|
================================================
|image2|
Starting with failure. Our test isn't there yet.
================================================
|
| Good. This fails. It's because we don't have a test called "def
test_ellipse" in there yet. What there is, is a method called
'todo_test_ellipse'. This is an extension pygame testing framework has
so we can easily see which functionality we still need to write tests
for.
::
~/pygame/ $ python -m pygame.tests --incomplete
| ...
| FAILED (errors=39)
| Looks like there are currently 39 functions or methods without a test.
Easy pickings.
Python 3 to the rescue.
-----------------------
| Tip: Python 3.7 makes it easier to run tests with the magic "-k"
argument. With this you can run tests that match a substring. So to
run all the tests with "ellipse" in their name you can do this:
::
~pygame/ $ python3 test/draw_test.py -k ellipse
|
Digression: Good first issue, low hanging fruit, and help wanted.
-----------------------------------------------------------------
.. container::
*Something that's easy to do.*
|
| A little digression for a moment... what is a good first issue?
| Low hanging fruit is easy to get off the tree. You don't need a
ladder, or robot arms with a claw on the end. So I guess that's what
people are talking about in the programming world when they say "low
hanging fruit".
=============================================================================================================================
|image3|
`pygame low hanging fruit <https://github.com/pygame/pygame/issues?q=is%3Aissue+is%3Aopen+label%3A%22Low+Hanging+Fruit%22>`__
=============================================================================================================================
|
| Many projects keep a list of "good first issue", "low hanging fruit",
or "help wanted" labeled issues. Like the `pygame "good first
issue" <https://github.com/pygame/pygame/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22>`__
list. Ones other people don't think will be all that super hard to do.
If you can't find any on there labeled like this, then ask them.
Perhaps they'll know of something easy to do, but haven't had the time
to mark one yet.
| One little trick is that writing a simple test is quite easy for most
projects. So if they don't have any marked "low hanging fruit", or
"good first issue" go take a look in their test folder and see if you
can add something in there.
| Don't be afraid to ask questions. If you look at an issue, and you
can't figure it out, or get stuck on something, ask a nice question in
there for help.
Digression: Contribution guide.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| There's usually also a contribution guide. Like the `pygame
Contribute <https://www.pygame.org/wiki/Contribute>`__ wiki page. Or
it may be called developer docs, or there may be a CONTRIBUTING.md
file in the source code repository. Often there is a separate place
the developers talk on. For pygame it is the pygame mailing list, but
there is also a chat server which is a bit more informal.
A full example of a test.
-------------------------
| The unittest module arranges tests inside functions that start with
"test_" that live in a class.
| Here is a full example:
::
import unittest
class TestEllipse(unittest.TestCase):
def test_ellipse(self):
import pygame.draw
surf = pygame.Surface((320, 200))
pygame.draw.ellipse(surf, (255, 0, 0), (10, 10, 25, 20))
if __name__ == '__main__':
unittest.main()
|
| You can save that in a file yourself(test_draw1.py for example) and
run it to see if it passes.
Committing your test, and making a Pull Request.
------------------------------------------------
| Here you need to make sure you have "git" setup. Also you should have
"forked" the repo you want to make changes on, and done a 'git clone'
of it.
::
# create a "branch"
git checkout -b my-draw-test-branch
# save your changes locally.
git commit test/draw_test.py -m "test for the draw.ellipse function"
# push your changes
git push origin my-draw-test-branch
|
| Here we see a screenshot of a terminal running these commands.
======================================================================
|image4|
Here we see the commands to commit something and push it up to a repo.
======================================================================
| When you push your changes, it will print out some progress, and then
give you a URL at which you can create a "pull request".
| When you git push it prints out these instructions:
::
remote: Create a pull request for 'my-draw-test-branch' on GitHub by visiting:
remote: https://github.com/YOURUSERNAME/pygame/pull/new/my-draw-test-branch
|
| You can also go to your online fork to create a pull request there.
Writing your pull request text.
-------------------------------
| When you create a pull request, you are saying "hey, I made these
changes. Do you want them? What do you think? Do you want me to change
anything? Is this ok?"
| It's usually good to link your pull request to an "issue". Maybe
you're starting to fix an existing problem with the code.
============================================================================================
|image5|
Different "checks" are run by robots to try and catch problems before the code is merged in.
============================================================================================
|
Testing the result with assertEquals.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| How about it we want to test if the draw function actually draws
something?
| Put this code into test_draw2.py
::
import unittest
class TestEllipse(unittest.TestCase):
def test_ellipse(self):
import pygame.draw
black = pygame.Color('black')
red = pygame.Color('red')
surf = pygame.Surface((320, 200))
surf.fill(black)
# The area the ellipse is contained in, is held by rect.
#
# 10 pixels from the left,
# 11 pixels from the top.
# 225 pixels wide.
# 95 pixels high.
rect = (10, 11, 225, 95)
pygame.draw.ellipse(surf, red, rect)
# To see what is drawn you can save the image.
# pygame.image.save(surf, "test_draw2_image.png")
# The ellipse should not draw over the black in the top left spot.
self.assertEqual(surf.get_at((0, 0)), black)
# It should be red in the middle of the ellipse.
middle_of_ellipse = (125, 55)
self.assertEqual(surf.get_at(middle_of_ellipse), red)
if __name__ == '__main__':
unittest.main()
|
======================================
|image6|
Red ellipse drawn at (10, 11, 225, 95)
======================================
|
What is a git for? Jargon.
--------------------------
| `jargon <https://en.wikipedia.org/wiki/Jargon_File>`__ - internet
slang used by programmers. Rather than use a paragraph to explain
something, people made up all sorts of strange words and phrases.
| `git <https://en.wikipedia.org/wiki/Git>`__ - for sharing versions of
source code. It lets people work together, and provides tools for
people to.
| `pull
request <https://en.wikipedia.org/wiki/Distributed_version_control#Pull_requests>`__
(PR) - "Dear everyone, I request that you **git pull** my commits.". A
pull request is a conversation starter. "Hey, I made a PR. Can you
have a look?". When you "**git push**" your commits (upload your
changes).
| `unit test <https://en.wikipedia.org/wiki/Unit_testing>`__ - **does
this thing**\ (*unit*) **even work**\ (*test*)?!!? A program to test
if another program works (how you think it should). Rather than test
manually over and over again, a unit test can be written and then
automatically test your code. A unit test is a nice example of how to
use what you've made too. So when you do a pull request the people
looking at it know what the code is supposed to do, and that the
machine has already checked the code works for them.
| `assert <https://en.wikipedia.org/wiki/Assertion_(software_development)>`__
- "assert 1 == 1". An assert is saying something is true. "I assert
that one equals one!". You can also assert variables.
.. |image0| image:: https://web.archive.org/web/20191007225130im_/https://4.bp.blogspot.com/-gyvG9zFHcEk/Wor9d2kSQAI/AAAAAAAABFE/f2aTbAp4bcc9ySdgv0MGTaQ4TtLmpyq-QCLcBGAs/s320/Screen%2BShot%2B2018-02-19%2Bat%2B17.38.48.png
:width: 320px
:height: 122px
:target: https://4.bp.blogspot.com/-gyvG9zFHcEk/Wor9d2kSQAI/AAAAAAAABFE/f2aTbAp4bcc9ySdgv0MGTaQ4TtLmpyq-QCLcBGAs/s1600/Screen%2BShot%2B2018-02-19%2Bat%2B17.38.48.png
.. |image1| image:: https://web.archive.org/web/20191007225130im_/https://2.bp.blogspot.com/-iz0rO6Hcols/WosBFD23pnI/AAAAAAAABFc/V0xlNrJtGyc33utyDR3csDI7HIRFq9M2ACLcBGAs/s320/Screen%2BShot%2B2018-02-19%2Bat%2B17.53.39.png
:width: 150px
:height: 320px
:target: https://2.bp.blogspot.com/-iz0rO6Hcols/WosBFD23pnI/AAAAAAAABFc/V0xlNrJtGyc33utyDR3csDI7HIRFq9M2ACLcBGAs/s1600/Screen%2BShot%2B2018-02-19%2Bat%2B17.53.39.png
.. |image2| image:: https://web.archive.org/web/20191007225130im_/https://2.bp.blogspot.com/-xo9yW8cGce0/Wpz9n2OQAxI/AAAAAAAABIA/nZz5eqLvScA6l_LAEU1tAgFjI8OE4KmigCLcBGAs/s320/Screen%2BShot%2B2018-03-05%2Bat%2B09.20.52.png
:width: 320px
:height: 84px
:target: https://2.bp.blogspot.com/-xo9yW8cGce0/Wpz9n2OQAxI/AAAAAAAABIA/nZz5eqLvScA6l_LAEU1tAgFjI8OE4KmigCLcBGAs/s1600/Screen%2BShot%2B2018-03-05%2Bat%2B09.20.52.png
.. |image3| image:: https://web.archive.org/web/20191007225130im_/https://4.bp.blogspot.com/-JTUSwRkfQaw/Wp0NboqPpoI/AAAAAAAABIc/JgmiFP9JXHw7DZnm0YZyG85KpzuUaLxBQCLcBGAs/s320/Screen%2BShot%2B2018-03-05%2Bat%2B10.28.10.png
:width: 320px
:height: 196px
:target: https://github.com/pygame/pygame/issues?q=is%3Aissue+is%3Aopen+label%3A%22Low+Hanging+Fruit%22
.. |image4| image:: https://1.bp.blogspot.com/-PBgTIGajntM/XcByoZiWrrI/AAAAAAAABgM/D6PlY5nVBjc1PCfsOxvncfnTnZf9A64LACLcBGAsYHQ/s400/Screen%2BShot%2B2019-11-04%2Bat%2B19.48.45.png
:width: 400px
:height: 277px
:target: https://1.bp.blogspot.com/-PBgTIGajntM/XcByoZiWrrI/AAAAAAAABgM/D6PlY5nVBjc1PCfsOxvncfnTnZf9A64LACLcBGAsYHQ/s1600/Screen%2BShot%2B2019-11-04%2Bat%2B19.48.45.png
.. |image5| image:: https://1.bp.blogspot.com/-ud2zAwHygoo/XcBlS1SCUbI/AAAAAAAABfY/nQvFaKw6RIEHwswR0eskSd8oV2aYmXqOgCLcBGAsYHQ/s320/Screen%2BShot%2B2019-11-04%2Bat%2B18.51.37.png
:width: 320px
:height: 167px
:target: https://1.bp.blogspot.com/-ud2zAwHygoo/XcBlS1SCUbI/AAAAAAAABfY/nQvFaKw6RIEHwswR0eskSd8oV2aYmXqOgCLcBGAsYHQ/s1600/Screen%2BShot%2B2019-11-04%2Bat%2B18.51.37.png
.. |image6| image:: https://1.bp.blogspot.com/-nGgcx-0GkTY/XcBwYZ-srgI/AAAAAAAABfw/dr7mbMqU8cMVkea7tJDV-SdbScUrGFjKwCLcBGAsYHQ/s1600/test_draw2_image.png
:target: https://1.bp.blogspot.com/-nGgcx-0GkTY/XcBwYZ-srgI/AAAAAAAABfw/dr7mbMqU8cMVkea7tJDV-SdbScUrGFjKwCLcBGAsYHQ/s1600/test_draw2_image.png