forked from rachelslaybaugh/python-testing
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path06-edges.html
More file actions
162 lines (153 loc) · 10.6 KB
/
06-edges.html
File metadata and controls
162 lines (153 loc) · 10.6 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
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
<!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">Edge and Corner Cases</h2>
<section class="objectives panel panel-warning">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-certificate"></span>Learning Objectives</h2>
</div>
<div class="panel-body">
<ul>
<li>Understand that edge cases are at the limit of the function’s behavior</li>
<li>Write a test for an edge case</li>
<li>Understand that corner cases are where two edge cases meet</li>
<li>Write a test for a corner case</li>
</ul>
</div>
</section>
<p>What we saw in the tests for the mean function are called <em>interior</em> tests. The precise points that we tested did not matter. The mean function should have behaved as expected when it is within the valid range.</p>
<h2 id="edge-cases">Edge Cases</h2>
<p>The situation where the test examines either the beginning or the end of a range, but not the middle, is called an <em>edge case</em>. In a simple, one-dimensional problem, the two edge cases should always be tested along with at least one internal point. This ensures that you have good <em>coverage</em> over the range of values.</p>
<p>Anecdotally, it is important to test edges cases because this is where errors tend to arise. Qualitatively different behavior happens at boundaries. As such, they tend to have special code dedicated to them in the implementation.</p>
<aside class="callout panel panel-info">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pushpin"></span>Consider the Fibonacci Sequence</h2>
</div>
<div class="panel-body">
<p>Take a moment to recall everything you know about the Fibonacci sequence.</p>
<p>The fibonacci sequence is valid for all positive integers. To believe that a fibonacci sequence function is accurate throughout that space, is it necessary to check every expected output value of the fibonacci sequence? Given that the sequence is infinite, let’s hope not.</p>
<p>Indeed, what we should probably do is test a few values within the typical scope of the function, and then test values at the limit of the function’s behavior.</p>
</div>
</aside>
<p>Consider the following simple Fibonacci function:</p>
<pre class="sourceCode python"><code class="sourceCode python"><span class="kw">def</span> fib(n):
<span class="kw">if</span> n == <span class="dv">0</span> or n == <span class="dv">1</span>:
<span class="kw">return</span> <span class="dv">1</span>
<span class="kw">else</span>:
<span class="kw">return</span> fib(n - <span class="dv">1</span>) + fib(n - <span class="dv">2</span>)</code></pre>
<p>This function has two edge cases: zero and one. For these values of <code>n</code>, the <code>fib()</code> function does something special that does not apply to any other values. Such cases should be tested explicitly. A minimally sufficient test suite for this function would be:</p>
<pre class="sourceCode python"><code class="sourceCode python"><span class="ch">from</span> nose.tools <span class="ch">import</span> assert_equal
<span class="ch">from</span> mod <span class="ch">import</span> fib
<span class="kw">def</span> test_fib0():
<span class="co"># test edge 0</span>
obs = fib(<span class="dv">0</span>)
assert_equal(<span class="dv">1</span>, obs)
<span class="kw">def</span> test_fib1():
<span class="co"># test edge 1</span>
obs = fib(<span class="dv">1</span>)
assert_equal(<span class="dv">1</span>, obs)
<span class="kw">def</span> test_fib6():
<span class="co"># test internal point</span>
obs = fib(<span class="dv">6</span>)
assert_equal(<span class="dv">13</span>, obs)</code></pre>
<p>Different functions will have different edge cases. Often, you need not test for cases that are outside the valid range, unless you want to test that the function fails. In the <code>fib()</code> function negative and noninteger values are not valid inputs. Tests for these classes of numbers serve you well if you want to make sure that the function fails as expected. Indeed, we learned in the assertions section that this is actually quite a good idea.</p>
<section class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Test for Graceful Failure</h2>
</div>
<div class="panel-body">
<p>The <code>fib()</code> function should probably return the Python built-in <code>NotImplemented</code> value for negative and noninteger values.</p>
<ol style="list-style-type: decimal">
<li>Create a file called <code>test_fib.py</code></li>
<li>Copy the three tests above into that file.</li>
<li>Write <strong>two new</strong> tests that check for the expected return value (NotImplemented) in each case (for negative input and noninteger input respectively).</li>
</ol>
</div>
</section>
<p>Edge cases are not where the story ends, though, as we will see next.</p>
<h2 id="corner-cases">Corner Cases</h2>
<p>When two or more edge cases are combined, it is called a <em>corner case</em>. If a function is parametrized by two linear and independent variables, a test that is at the extreme of both variables is in a corner. As a demonstration, consider the case of the function <code>(sin(x) / x) * (sin(y) / y)</code>, presented here:</p>
<pre class="sourceCode python"><code class="sourceCode python"><span class="ch">import</span> numpy <span class="ch">as</span> np
<span class="kw">def</span> sinc2d(x, y):
<span class="kw">if</span> x == <span class="fl">0.0</span> and y == <span class="fl">0.0</span>:
<span class="kw">return</span> <span class="fl">1.0</span>
<span class="kw">elif</span> x == <span class="fl">0.0</span>:
<span class="kw">return</span> np.sin(y) / y
<span class="kw">elif</span> y == <span class="fl">0.0</span>:
<span class="kw">return</span> np.sin(x) / x
<span class="kw">else</span>:
<span class="kw">return</span> (np.sin(x) / x) * (np.sin(y) / y)</code></pre>
<p>The function <code>sin(x)/x</code> is called the <code>sinc()</code> function. We know that at the point where <code>x = 0</code>, then <code>sinc(x) == 1.0</code>. In the code just shown, <code>sinc2d()</code> is a two-dimensional version of this function. When both <code>x</code> and <code>y</code> are zero, it is a corner case because it requires a special value for both variables. If either <code>x</code> or <code>y</code> but not both are zero, these are edge cases. If neither is zero, this is a regular internal point.</p>
<p>A minimal test suite for this function would include a separate test for the each of the edge cases, and an internal point. For example:</p>
<pre class="sourceCode python"><code class="sourceCode python"><span class="ch">import</span> numpy <span class="ch">as</span> np
<span class="ch">from</span> nose.tools <span class="ch">import</span> assert_equal
<span class="ch">from</span> mod <span class="ch">import</span> sinc2d
<span class="kw">def</span> test_internal():
exp = (<span class="fl">2.0</span> / np.pi) * (-<span class="fl">2.0</span> / (<span class="fl">3.0</span> * np.pi))
obs = sinc2d(np.pi / <span class="fl">2.0</span>, <span class="fl">3.0</span> * np.pi / <span class="fl">2.0</span>)
assert_equal(exp, obs)
<span class="kw">def</span> test_edge_x():
exp = (-<span class="fl">2.0</span> / (<span class="fl">3.0</span> * np.pi))
obs = sinc2d(<span class="fl">0.0</span>, <span class="fl">3.0</span> * np.pi / <span class="fl">2.0</span>)
assert_equal(exp, obs)
<span class="kw">def</span> test_edge_y():
exp = (<span class="fl">2.0</span> / np.pi)
obs = sinc2d(np.pi / <span class="fl">2.0</span>, <span class="fl">0.0</span>)
assert_equal(exp, obs)</code></pre>
<section class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Write a Corner Case</h2>
</div>
<div class="panel-body">
<p>The sinc2d example will also need a test for the corner case, where both x and y are 0.0.</p>
<ol style="list-style-type: decimal">
<li>Insert the sinc2d function code (above) into a file called mod.py.</li>
<li>Add the edge and internal case tests (above) to a test_sinc2d.py file.</li>
<li>Invent and implement a corner case test in that file.</li>
<li>Run all of the tests using <code>nosetests</code> on the command line.</li>
</ol>
</div>
</section>
<p>Corner cases can be even trickier to find and debug than edge cases because of their increased complexity. This complexity, however, makes them even more important to explicitly test.</p>
<p>Whether internal, edge, or corner cases, we have started to build up a classification system for the tests themselves. In the following sections, we will build this system up even more based on the role that the tests have in the software architecture.</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="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>