-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest-driven-api-integration-with-go.html
290 lines (262 loc) · 29.2 KB
/
test-driven-api-integration-with-go.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700,400italic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="http://projects.curiousllc.com/theme/css/style.min.css">
<link rel="stylesheet" type="text/css" href="http://projects.curiousllc.com/theme/css/pygments.min.css">
<link rel="stylesheet" type="text/css" href="http://projects.curiousllc.com/theme/css/font-awesome.min.css">
<link href="http://projects.curiousllc.com/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Working with hardware Atom">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="" />
<meta name="author" content="ryanday" />
<meta name="description" content="Test driven API integration" />
<meta name="keywords" content="golang, tdd">
<meta property="og:site_name" content="Working with hardware"/>
<meta property="og:title" content="Test driven API integration with Go"/>
<meta property="og:description" content="Test driven API integration"/>
<meta property="og:locale" content="en_US"/>
<meta property="og:url" content="http://projects.curiousllc.com/test-driven-api-integration-with-go.html"/>
<meta property="og:type" content="article"/>
<meta property="article:published_time" content="2015-05-25 20:05:00-04:00"/>
<meta property="article:modified_time" content=""/>
<meta property="article:author" content="http://projects.curiousllc.com/author/ryanday.html">
<meta property="article:section" content="programming"/>
<meta property="article:tag" content="golang"/>
<meta property="article:tag" content="tdd"/>
<meta property="og:image" content="https://avatars3.githubusercontent.com/u/6845642?v=3&s=200"> <title>Working with hardware – Test driven API integration with Go</title>
</head>
<body>
<aside>
<div>
<a href="http://projects.curiousllc.com">
<img src="https://avatars3.githubusercontent.com/u/6845642?v=3&s=200" alt="" title="">
</a>
<h1><a href="http://projects.curiousllc.com"></a></h1>
<p></p>
<nav>
<div style="text-align: left; font-size: 0.9em">At Curious, we connect Things to the Cloud. <br/><br/>
If you would like to launch a product, or connect your product to the Cloud, drop us a line!</div>
<ul class="list">
<li><a href="mailto:[email protected]" target="_blank">[email protected]</a></li>
<li><a href="http://curiousllc.com/" target="_blank">Curious LLC</a></li>
<li><a href="http://www.meetup.com/NOVA-Python" target="_blank">Nova Python</a></li>
<li><a href="http://www.meetup.com/Golang-DC/" target="_blank">Golang DC</a></li>
</ul>
</nav>
<ul class="social">
<li><a class="sc-You can add links in your config file" href="#" target="_blank"><i class="fa fa-You can add links in your config file"></i></a></li>
<li><a class="sc-Another social link" href="#" target="_blank"><i class="fa fa-Another social link"></i></a></li>
</ul>
</div>
</aside>
<main>
<article>
<header>
<h1 id="test-driven-api-integration-with-go">Test driven API integration with Go</h1>
<p>
Posted on Mon 25 May 2015 in <a href="http://projects.curiousllc.com/category/programming.html">programming</a> by <a href="http://projects.curiousllc.com/author/ryanday.html">ryanday</a>
• Tagged with
<a href="http://projects.curiousllc.com/tag/golang.html">golang</a>, <a href="http://projects.curiousllc.com/tag/tdd.html">tdd</a>
</p>
</header>
<div>
<p>Test Driven design is more than simply testing individual functions before you write then. There are several components of your API client that you want to address:</p>
<ul class="simple">
<li>How the workflow will appear from a user's perspective.</li>
<li>How the API meta control will work (pages, a cursor, offset/limit, authenticate, this sort of thing).</li>
<li>How the backend API developers will shake their heads as you call the same method over and over during testing.</li>
</ul>
<p>We want to find a testing method that solves all these concerns. We should verify our core functionality is working. Then we can play with the user's workflow and test out different ideas without breaking that functionality. We can implement caching, iterating large data, or pieces of data, on top of the API after the tests are complete. Having the tests in place gives us the freedom to experiment.</p>
<p>As always, let's check out Github to see how others are testing their client side APIs. There are lots of examples of course, but the common methods boil down to:</p>
<ul class="simple">
<li>Use a key and the live API (<a class="reference external" href="https://github.com/ChimeraCoder/anaconda/blob/master/twitter_test.go">twitter</a>)</li>
<li>Don't test the actual calls, just the response handling (<a class="reference external" href="https://github.com/facebookgo/fbapi">facebook</a>)</li>
<li>Mock very short responses to test calls (<a class="reference external" href="https://github.com/MariaTerzieva/gotumblr/blob/master/gotumblr_test.go">tumblr</a>, <a class="reference external" href="https://github.com/masci/flickr/blob/master/testutils.go">flikr</a>)</li>
<li>Use a mix of mocks and live (<a class="reference external" href="https://github.com/google/go-github/tree/master/tests">google github</a>)</li>
</ul>
<p>Each of these examples is worth taking a look at. Of these, Google's seems the most complete. Their test setup creates a global Mux and httptest.Server for that Mux. Each unit test attaches a handler to the Mux. They also have live tests that don't run in their CI. These are specifically to test whether server side functionality has changed.</p>
<p><strong>Mocking the calls</strong></p>
<p>Really, we want to test the API as the user would consume it. We don't want to simply test all the little pieces and assume a nice workflow. So we need to write a test like the user would write their code.</p>
<div class="highlight"><pre><span class="nx">api</span> <span class="o">:=</span> <span class="nx">CreateNewApi</span><span class="p">(</span><span class="o">&</span><span class="nx">client</span><span class="p">)</span>
<span class="nx">controlChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">bool</span><span class="p">)</span>
<span class="nx">responseChan</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">api</span><span class="p">.</span><span class="nx">SomeMethod</span><span class="p">(</span><span class="nx">controlChan</span><span class="p">)</span>
</pre></div>
<p>CreateNewApi() most likely has us speaking with the actual API server. The API url is hardcoded in the library somewhere. So our test will probably hit the live server. Using the live API for testing violates my third concern. Fortunately we can rewrite the client's transport method to point to a test server. We do this with http.RoundTripper.</p>
<div class="highlight"><pre><span class="kd">type</span> <span class="nx">RewriteTransport</span> <span class="kd">struct</span> <span class="p">{</span>
<span class="nx">Transport</span> <span class="nx">http</span><span class="p">.</span><span class="nx">RoundTripper</span>
<span class="nx">URL</span> <span class="o">*</span><span class="nx">url</span><span class="p">.</span><span class="nx">URL</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="nx">RewriteTransport</span><span class="p">)</span> <span class="nx">RoundTrip</span><span class="p">(</span><span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// note that url.URL.ResolveReference doesn't work here</span>
<span class="c1">// since t.u is an absolute url</span>
<span class="nx">req</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Scheme</span> <span class="p">=</span> <span class="nx">t</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Scheme</span>
<span class="nx">req</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Host</span> <span class="p">=</span> <span class="nx">t</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Host</span>
<span class="nx">req</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Path</span> <span class="p">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">Join</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Path</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Path</span><span class="p">)</span>
<span class="nx">rt</span> <span class="o">:=</span> <span class="nx">t</span><span class="p">.</span><span class="nx">Transport</span>
<span class="k">if</span> <span class="nx">rt</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
<span class="nx">rt</span> <span class="p">=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">DefaultTransport</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">rt</span><span class="p">.</span><span class="nx">RoundTrip</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">client</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span>
<span class="nx">Transport</span><span class="p">:</span> <span class="nx">RewriteTransport</span><span class="p">{</span>
<span class="nx">URL</span><span class="p">:</span> <span class="nx">testUrl</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
</pre></div>
<p><strong>Minimal response test</strong></p>
<p>With the custom transport, we are able to implement handlers for our tests. These handlers can return minimal expected responses. Take Flickr for example:</p>
<div class="highlight"><pre><span class="c1">// https://github.com/masci/flickr/blob/master/response_test.go</span>
<span class="kd">func</span> <span class="nx">TestExtra</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">bodyStr</span> <span class="o">:=</span> <span class="s">`<?xml version="1.0" encoding="utf-8" ?></span>
<span class="s"> <rsp stat="ok"></span>
<span class="s"> <user id="23148015@N00"></span>
<span class="s"> <username>Massimiliano Pippi</username></span>
<span class="s"> </user></span>
<span class="s"> <brands></span>
<span class="s"> <brand id="apple">Apple</brand></span>
<span class="s"> </brands></span>
<span class="s"> </rsp>`</span>
<span class="nx">flickrResp</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">BasicResponse</span><span class="p">{}</span>
<span class="nx">response</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">{}</span> <span class="c1">// HL</span>
<span class="nx">response</span><span class="p">.</span><span class="nx">Body</span> <span class="p">=</span> <span class="nx">NewFakeBody</span><span class="p">(</span><span class="nx">bodyStr</span><span class="p">)</span> <span class="c1">// HL</span>
<span class="nx">err</span> <span class="o">:=</span> <span class="nx">parseApiResponse</span><span class="p">(</span><span class="nx">response</span><span class="p">,</span> <span class="nx">flickrResp</span><span class="p">)</span> <span class="c1">// HL</span>
<span class="nx">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="nx">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">flickrResp</span><span class="p">.</span><span class="nx">Extra</span> <span class="o">!=</span> <span class="s">""</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
<p>Flickr uses a transport rewrite to provide a minimal response to test. This gets us away from using the live API to test data. But the method being testing is parseApiResponse(). This isn't how the user will implement the API.</p>
<p>This Tumblr API uses the same minimal response method. It also tests functions in the same way a user would call them.</p>
<div class="highlight"><pre><span class="c1">// https://github.com/MariaTerzieva/gotumblr/blob/master/gotumblr_test.go</span>
<span class="kd">func</span> <span class="nx">TestFollowing</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">setup</span><span class="p">()</span>
<span class="k">defer</span> <span class="nx">teardown</span><span class="p">()</span>
<span class="nx">handleFunc</span><span class="p">(</span><span class="s">"/v2/user/following"</span><span class="p">,</span> <span class="s">"GET"</span><span class="p">,</span> <span class="s">`{"response": {"total_blogs": 1}}`</span><span class="p">,</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{},</span> <span class="nx">t</span><span class="p">)</span>
<span class="nx">following</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nx">Following</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">{}).</span><span class="nx">Total_blogs</span>
<span class="nx">want</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">following</span> <span class="o">!=</span> <span class="nx">want</span> <span class="p">{</span>
<span class="nx">t</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">"Following returned %+v, want %v"</span><span class="p">,</span> <span class="nx">following</span><span class="p">,</span> <span class="nx">want</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p><strong>Hosting test data</strong></p>
<p>This is a method I've been using that I haven't seen in the wild. It takes advantage of all the API consoles out there. You make a call using the provider's API console in a sandbox, and copy the (usually) JSON response into your test_data/ directory. Then simply serve the appropriate JSON file during the test.</p>
<p>Using a recent integration for the Lingotek API, here is an example directory structure:</p>
<div class="highlight"><pre>src/
lingotek/
test_data/
projects_full.json
projects_three.json
</pre></div>
<p>These two json files contain a full API response, including headers, offset/limit/total counts, and other meta information. In our mock handler, we can control which of those files is served based on query parameters.</p>
<p>To begin, we have a function which takes a channel for returning a *http.Request, and a function which returns the name of the file which contains our appropriate response.</p>
<div class="highlight"><pre><span class="kd">func</span> <span class="nx">createTestServer</span><span class="p">(</span><span class="nx">requestChan</span> <span class="kd">chan</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">getFileName</span> <span class="kd">func</span><span class="p">(</span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">httptest</span><span class="p">.</span><span class="nx">Server</span><span class="p">,</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">handler</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">testData</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span>
<span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span>
<span class="c1">// getFileName will determine which response to load based on the request</span>
<span class="nx">testData</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Open</span><span class="p">(</span><span class="nx">getFileName</span><span class="p">(</span><span class="nx">r</span><span class="p">))</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
<span class="nx">http</span><span class="p">.</span><span class="nx">NotFound</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nx">io</span><span class="p">.</span><span class="nx">Copy</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">testData</span><span class="p">)</span>
<span class="nx">requestChan</span> <span class="o"><-</span> <span class="nx">r</span>
<span class="p">})</span>
<span class="nx">server</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nx">NewServer</span><span class="p">(</span><span class="nx">handler</span><span class="p">)</span>
<span class="nx">testUrl</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">url</span><span class="p">.</span><span class="nx">Parse</span><span class="p">(</span><span class="nx">server</span><span class="p">.</span><span class="nx">URL</span><span class="p">)</span>
<span class="nx">client</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span>
<span class="nx">Transport</span><span class="p">:</span> <span class="nx">RewriteTransport</span><span class="p">{</span>
<span class="nx">URL</span><span class="p">:</span> <span class="nx">testUrl</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="c1">// Return our test server and a client which points to it</span>
<span class="k">return</span> <span class="nx">server</span><span class="p">,</span> <span class="nx">client</span>
<span class="p">}</span>
</pre></div>
<p>Using this test server and client, we write a test like this:</p>
<div class="highlight"><pre><span class="kd">func</span> <span class="nx">TestGetProjects</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// This is a simple handler, we always return the same response file</span>
<span class="nx">f</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">"test_data/projects_full.json"</span>
<span class="p">}</span>
<span class="nx">rCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nx">server</span><span class="p">,</span> <span class="nx">client</span> <span class="o">:=</span> <span class="nx">createTestServer</span><span class="p">(</span><span class="nx">rCh</span><span class="p">,</span> <span class="nx">f</span><span class="p">)</span>
<span class="k">defer</span> <span class="nx">server</span><span class="p">.</span><span class="nx">Close</span><span class="p">()</span>
<span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">rCh</span><span class="p">)</span>
<span class="nx">api</span> <span class="o">:=</span> <span class="nx">NewApi</span><span class="p">(</span><span class="s">"dummyToken"</span><span class="p">,</span> <span class="o">&</span><span class="nx">client</span><span class="p">)</span>
<span class="nx">resp</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">api</span><span class="p">.</span><span class="nx">GetProjectsPage</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
<span class="nx">t</span><span class="p">.</span><span class="nx">Error</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">resp</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">10</span> <span class="p">{</span>
<span class="nx">t</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">"Expected 10 projects, got %d\n"</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">resp</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>But we can get more complex. Let's say we want to test our ListProject function. ListProject() returns a channel that the caller can iterate over. Since the ListProject API method only returns 10 objects at a time, our method must make the call again at a new offset. Then the method must send the new objects to the channel.</p>
<p>Our mockHandler function has inspect the request, and send a different response for the different offset.</p>
<div class="highlight"><pre><span class="nx">mockHandler</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">(</span><span class="nx">fileName</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">offset</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Query</span><span class="p">().</span><span class="nx">Get</span><span class="p">(</span><span class="s">"offset"</span><span class="p">)</span>
<span class="c1">// If our client requested the next page,</span>
<span class="c1">// send a different json response</span>
<span class="k">if</span> <span class="nx">offset</span> <span class="o">==</span> <span class="s">"10"</span> <span class="p">{</span>
<span class="nx">fileName</span> <span class="p">=</span> <span class="s">"test_data/projects_three.json"</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">fileName</span> <span class="p">=</span> <span class="s">"test_data/projects_full.json"</span>
<span class="p">}</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="cm">/* . . . */</span>
<span class="nx">doneChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">bool</span><span class="p">)</span>
<span class="nx">projectChan</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">api</span><span class="p">.</span><span class="nx">ListProjects</span><span class="p">(</span><span class="nx">doneChan</span><span class="p">)</span>
<span class="nx">cNum</span> <span class="o">:=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="nx">_</span> <span class="p">=</span> <span class="k">range</span> <span class="nx">projectChan</span> <span class="p">{</span>
<span class="nx">cNum</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">if</span> <span class="nx">cNum</span> <span class="o">!=</span> <span class="mi">13</span> <span class="p">{</span>
<span class="nx">t</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">"Expected 13 projects, got %d"</span><span class="p">,</span> <span class="nx">cNum</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
<p>This test verifies that we can iterate over a channel while the API method is making multiple calls to the backend.</p>
<p>Full source is on <a class="reference external" href="https://github.com/CuriousLLC/Lingotek">Github</a>.</p>
</div>
<div class="tag-cloud">
<p>
<a href="http://projects.curiousllc.com/tag/golang.html">golang</a>
<a href="http://projects.curiousllc.com/tag/tdd.html">tdd</a>
</p>
</div>
</article>
<footer>
<p>© Ryan Day </p>
<p>Built using <a href="http://getpelican.com" target="_blank">Pelican</a> - <a href="https://github.com/alexandrevicenzi/flex" target="_blank">Flex</a> theme by <a href="http://alexandrevicenzi.com" target="_blank">Alexandre Vicenzi</a></p> </footer>
</main>
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-2393416-13', 'auto');
ga('send', 'pageview');
</script>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "BlogPosting",
"name": "Test driven API integration with Go",
"headline": "Test driven API integration with Go",
"datePublished": "2015-05-25 20:05:00-04:00",
"dateModified": "",
"author": {
"@type": "Person",
"name": "ryanday",
"url": "http://projects.curiousllc.com/author/ryanday.html"
},
"image": "https://avatars3.githubusercontent.com/u/6845642?v=3&s=200",
"url": "http://projects.curiousllc.com/test-driven-api-integration-with-go.html",
"description": "Test driven API integration"
}
</script></body>
</html>