forked from carpentries-incubator/python-testing
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path04-units.html
More file actions
130 lines (124 loc) · 8.84 KB
/
04-units.html
File metadata and controls
130 lines (124 loc) · 8.84 KB
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<title>Software Carpentry: Testing</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap-theme.css" />
<link rel="stylesheet" type="text/css" href="css/swc.css" />
<link rel="alternate" type="application/rss+xml" title="Software Carpentry Blog" href="http://software-carpentry.org/feed.xml"/>
<meta charset="UTF-8" />
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body class="lesson">
<div class="container card">
<div class="banner">
<a href="http://software-carpentry.org" title="Software Carpentry">
<img alt="Software Carpentry banner" src="img/software-carpentry-banner.png" />
</a>
</div>
<article>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<a href="index.html"><h1 class="title">Testing</h1></a>
<h2 class="subtitle">Unit Tests</h2>
<section class="objectives panel panel-warning">
<div class="panel-heading">
<h2 id="learning-objectives"><span class="glyphicon glyphicon-certificate"></span>Learning Objectives</h2>
</div>
<div class="panel-body">
<ul>
<li>Understand that functions are the atomistic unit of software.</li>
<li>Understand that simpler units are easier to test than complex ones.</li>
<li>Understand how to write a single unit test.</li>
<li>Understand how to run a single unit test.</li>
<li>Understand how test fixtures can help write tests.</li>
</ul>
</div>
</section>
<p>Unit tests are so called because they exercise the functionality of the code by interrogating individual functions and methods. Functions and methods can often be considered the atomic units of software because they are indivisible. However, what is considered to be the smallest code <em>unit</em> is subjective. The body of a function can be long are short, and shorter functions are arguably more unit-like than long ones.</p>
<p>Thus what reasonably constitutes a code unit typically varies from project to project and language to language. A good guideline is that if the code cannot be made any simpler logically (you cannot split apart the addition operator) or practically (a function is self-contained and well defined), then it is a unit.</p>
<aside class="callout panel panel-info">
<div class="panel-heading">
<h2 id="functions-are-like-paragraphs"><span class="glyphicon glyphicon-pushpin"></span>Functions are Like Paragraphs</h2>
</div>
<div class="panel-body">
<p>Recall that humans can only hold a few ideas in our heads at once. Paragraphs in books, for example, become unwieldy after a few lines. Functions, generally, shouldn’t be longer than paragraphs. Robert C. Martin, the author of “Clean Code” said : “The first rule of functions is that <em>they should be small</em>. The second rule of functions is that <em>they should be smaller than that</em>.”</p>
</div>
</aside>
<p>The desire to unit test code often has the effect of encouraging both the code and the tests to be as small, well-defined, and modular as possible.<br />
In Python, unit tests typically take the form of test functions that call and make assertions about methods and functions in the code base. To run these test functions, a test framework is often required to collect them together. For now, we’ll write some tests for the mean function and simply run them individually to see whether they fail. In the next session, we’ll use a test framework to collect and run them.</p>
<h2 id="unit-tests-are-just-functions">Unit Tests Are Just Functions</h2>
<p>Unit tests are typically made of three pieces, some set-up, a number of assertions, and some tear-down. Set-up can be as simple as initializing the input values or as complex as creating and initializing concrete instances of a class. Ultimately, the test occurs when an assertion is made, comparing the observed and expected values. For example, let us test that our mean function successfully calculates the known value for a simple list.</p>
<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="im">from</span> mean <span class="im">import</span> <span class="op">*</span>
<span class="kw">def</span> test_ints():
num_list <span class="op">=</span> [<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>]
obs <span class="op">=</span> mean(num_list)
exp <span class="op">=</span> <span class="dv">3</span>
<span class="cf">assert</span> obs <span class="op">==</span> exp</code></pre></div>
<p>The test above: - sets up the input parameters (the simple list [1, 2, 3, 4, 5]. - collects the observed result - declares the expected result (calculated with our human brain). - and compares the two with an assertion.</p>
<p>A unit test suite is made up of many tests just like this one. A single implemented function may be tested in numerous ways.</p>
<section class="challenge panel panel-success">
<div class="panel-heading">
<h2 id="write-a-file-full-of-tests"><span class="glyphicon glyphicon-pencil"></span>Write a File Full of Tests</h2>
</div>
<div class="panel-body">
<ol style="list-style-type: decimal">
<li>In a file called <code>test_mean.py</code>, implement the following code:</li>
</ol>
<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="im">from</span> mean <span class="im">import</span> <span class="op">*</span>
<span class="kw">def</span> test_ints():
num_list <span class="op">=</span> [<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>]
obs <span class="op">=</span> mean(num_list)
exp <span class="op">=</span> <span class="dv">3</span>
<span class="cf">assert</span> obs <span class="op">==</span> exp
<span class="kw">def</span> test_zero():
num_list<span class="op">=</span>[<span class="dv">0</span>,<span class="dv">2</span>,<span class="dv">4</span>,<span class="dv">6</span>]
obs <span class="op">=</span> mean(num_list)
exp <span class="op">=</span> <span class="dv">3</span>
<span class="cf">assert</span> obs <span class="op">==</span> exp
<span class="kw">def</span> test_double():
<span class="co"># This one will fail in Python 2</span>
num_list<span class="op">=</span>[<span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>,<span class="dv">4</span>]
obs <span class="op">=</span> mean(num_list)
exp <span class="op">=</span> <span class="fl">2.5</span>
<span class="cf">assert</span> obs <span class="op">==</span> exp
<span class="kw">def</span> test_long():
big <span class="op">=</span> <span class="dv">100000000</span>
obs <span class="op">=</span> mean(<span class="bu">range</span>(<span class="dv">1</span>,big))
exp <span class="op">=</span> big<span class="op">/</span><span class="fl">2.0</span>
<span class="cf">assert</span> obs <span class="op">==</span> exp
<span class="kw">def</span> test_complex():
<span class="co"># given that complex numbers are an unordered field</span>
<span class="co"># the arithmetic mean of complex numbers is meaningless</span>
num_list <span class="op">=</span> [<span class="dv">2</span> <span class="op">+</span> 3j, <span class="dv">3</span> <span class="op">+</span> 4j, <span class="op">-</span><span class="dv">32</span> <span class="op">-</span> 2j]
obs <span class="op">=</span> mean(num_list)
exp <span class="op">=</span> <span class="va">NotImplemented</span>
<span class="cf">assert</span> obs <span class="op">==</span> exp</code></pre></div>
<ol start="2" style="list-style-type: decimal">
<li>Use IPython to import the test_mean package and run each test.</li>
</ol>
</div>
</section>
<p>Well, <strong>that</strong> was tedious.</p>
</div>
</div>
</article>
<div class="footer">
<a class="label swc-blue-bg" href="http://software-carpentry.org">Software Carpentry</a>
<a class="label swc-blue-bg" href="https://github.com/swcarpentry/lesson-template">Source</a>
<a class="label swc-blue-bg" href="mailto:admin@software-carpentry.org">Contact</a>
<a class="label swc-blue-bg" href="LICENSE.html">License</a>
</div>
</div>
<!-- Javascript placed at the end of the document so the pages load faster -->
<script src="http://software-carpentry.org/v5/js/jquery-1.9.1.min.js"></script>
<script src="css/bootstrap/bootstrap-js/bootstrap.js"></script>
</body>
</html>