Calculate the change (coins) to return to a customer when they buy something.
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/change.js:
--------------------------------------------------------------------------------
1 | var coins = [200, 100, 50, 20, 10, 5, 2, 1];
2 | /**
3 | * getChange accepts two parameters (totalPayable and cashPaid) and calculates
4 | * the change in "coins" that needs to be returned.
5 | * @param {number} payable the integer amount (in pennies) payable (to be paid)
6 | * @param {number} paid the integer amount (in pennies) the person paid
7 | * @returns {array} change the list of coins we need to dispense to the person
8 | * @example
9 | * getChange(215, 300); // returns [50, 20, 10, 5]
10 | */
11 | function getChange (payable, paid) {
12 | var change = [];
13 | var length = coins.length;
14 | var remaining = paid - payable; // we reduce this below
15 |
16 | for (var i = 0; i < length; i++) { // loop through array of notes & coins:
17 | var coin = coins[i];
18 |
19 | var times_coin_fits = Math.floor(remaining / coin); // no partial coins
20 | if(times_coin_fits >= 1) { // check coin fits into the remaining amount
21 |
22 | for(var j = 0; j < times_coin_fits ; j++) { // add coin to change x times
23 | change.push(coin);
24 | remaining = remaining - coin; // subtract coin from remaining
25 | }
26 | }
27 | }
28 | return change;
29 | };
30 |
31 | /* The code block below ONLY Applies to Node.js - This Demonstrates
32 | re-useability of JS code in both Back-end and Front-end! #isomorphic */
33 | /* istanbul ignore next */
34 | if (typeof module !== 'undefined' && module.exports) {
35 | module.exports = getChange; // allows CommonJS/Node.js require()
36 | }
37 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | /* istanbul ignore next */
2 | if (typeof module !== 'undefined' && module.exports) { // check we're server-side
3 | var QUnit = require('qunitjs'); // require QUnit node.js module
4 | // alias the QUnit.test method so we don't have to change all our tests
5 | var test = QUnit.test; // stores a copy of QUnit.test
6 | require('qunit-tap')(QUnit, console.log); // use console.log for test output
7 | var getChange = require('./change.js'); // load our getChange method
8 | }
9 |
10 | test('getChange(1,1) should equal [] - an empty array', function(assert) {
11 | var result = getChange(1, 1); //no change/coins just an empty array
12 | var expected = [];
13 | assert.deepEqual(result, expected);
14 | });
15 |
16 | test('getChange(215, 300) should return [50, 20, 10, 5]', function(assert) {
17 | var result = getChange(215, 300); // expect an array containing [50,20,10,5]
18 | var expected = [50, 20, 10, 5];
19 | assert.deepEqual(result, expected);
20 | });
21 |
22 | test('getChange(486, 600) should equal [100, 10, 2, 2]', function(assert) {
23 | var result = getChange(486, 600);
24 | var expected = [100, 10, 2, 2];
25 | assert.deepEqual(result, expected);
26 | });
27 |
28 | test('getChange(12, 400) should return [200, 100, 50, 20, 10, 5, 2, 1]', function(assert) {
29 | var result = getChange(12, 400);
30 | var expected = [200, 100, 50, 20, 10, 5, 2, 1];
31 | assert.deepEqual(result, expected);
32 | });
33 |
34 | /* istanbul ignore next */
35 | if (typeof module !== 'undefined' && module.exports) { QUnit.load(); } // run the tests
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License, version 2.0
2 |
3 | 1. Definitions
4 |
5 | 1.1. "Contributor"
6 |
7 | means each individual or legal entity that creates, contributes to the
8 | creation of, or owns Covered Software.
9 |
10 | 1.2. "Contributor Version"
11 |
12 | means the combination of the Contributions of others (if any) used by a
13 | Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 |
17 | means Covered Software of a particular Contributor.
18 |
19 | 1.4. "Covered Software"
20 |
21 | means Source Code Form to which the initial Contributor has attached the
22 | notice in Exhibit A, the Executable Form of such Source Code Form, and
23 | Modifications of such Source Code Form, in each case including portions
24 | thereof.
25 |
26 | 1.5. "Incompatible With Secondary Licenses"
27 | means
28 |
29 | a. that the initial Contributor has attached the notice described in
30 | Exhibit B to the Covered Software; or
31 |
32 | b. that the Covered Software was made available under the terms of
33 | version 1.1 or earlier of the License, but not also under the terms of
34 | a Secondary License.
35 |
36 | 1.6. "Executable Form"
37 |
38 | means any form of the work other than Source Code Form.
39 |
40 | 1.7. "Larger Work"
41 |
42 | means a work that combines Covered Software with other material, in a
43 | separate file or files, that is not Covered Software.
44 |
45 | 1.8. "License"
46 |
47 | means this document.
48 |
49 | 1.9. "Licensable"
50 |
51 | means having the right to grant, to the maximum extent possible, whether
52 | at the time of the initial grant or subsequently, any and all of the
53 | rights conveyed by this License.
54 |
55 | 1.10. "Modifications"
56 |
57 | means any of the following:
58 |
59 | a. any file in Source Code Form that results from an addition to,
60 | deletion from, or modification of the contents of Covered Software; or
61 |
62 | b. any new file in Source Code Form that contains any Covered Software.
63 |
64 | 1.11. "Patent Claims" of a Contributor
65 |
66 | means any patent claim(s), including without limitation, method,
67 | process, and apparatus claims, in any patent Licensable by such
68 | Contributor that would be infringed, but for the grant of the License,
69 | by the making, using, selling, offering for sale, having made, import,
70 | or transfer of either its Contributions or its Contributor Version.
71 |
72 | 1.12. "Secondary License"
73 |
74 | means either the GNU General Public License, Version 2.0, the GNU Lesser
75 | General Public License, Version 2.1, the GNU Affero General Public
76 | License, Version 3.0, or any later versions of those licenses.
77 |
78 | 1.13. "Source Code Form"
79 |
80 | means the form of the work preferred for making modifications.
81 |
82 | 1.14. "You" (or "Your")
83 |
84 | means an individual or a legal entity exercising rights under this
85 | License. For legal entities, "You" includes any entity that controls, is
86 | controlled by, or is under common control with You. For purposes of this
87 | definition, "control" means (a) the power, direct or indirect, to cause
88 | the direction or management of such entity, whether by contract or
89 | otherwise, or (b) ownership of more than fifty percent (50%) of the
90 | outstanding shares or beneficial ownership of such entity.
91 |
92 |
93 | 2. License Grants and Conditions
94 |
95 | 2.1. Grants
96 |
97 | Each Contributor hereby grants You a world-wide, royalty-free,
98 | non-exclusive license:
99 |
100 | a. under intellectual property rights (other than patent or trademark)
101 | Licensable by such Contributor to use, reproduce, make available,
102 | modify, display, perform, distribute, and otherwise exploit its
103 | Contributions, either on an unmodified basis, with Modifications, or
104 | as part of a Larger Work; and
105 |
106 | b. under Patent Claims of such Contributor to make, use, sell, offer for
107 | sale, have made, import, and otherwise transfer either its
108 | Contributions or its Contributor Version.
109 |
110 | 2.2. Effective Date
111 |
112 | The licenses granted in Section 2.1 with respect to any Contribution
113 | become effective for each Contribution on the date the Contributor first
114 | distributes such Contribution.
115 |
116 | 2.3. Limitations on Grant Scope
117 |
118 | The licenses granted in this Section 2 are the only rights granted under
119 | this License. No additional rights or licenses will be implied from the
120 | distribution or licensing of Covered Software under this License.
121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
122 | Contributor:
123 |
124 | a. for any code that a Contributor has removed from Covered Software; or
125 |
126 | b. for infringements caused by: (i) Your and any other third party's
127 | modifications of Covered Software, or (ii) the combination of its
128 | Contributions with other software (except as part of its Contributor
129 | Version); or
130 |
131 | c. under Patent Claims infringed by Covered Software in the absence of
132 | its Contributions.
133 |
134 | This License does not grant any rights in the trademarks, service marks,
135 | or logos of any Contributor (except as may be necessary to comply with
136 | the notice requirements in Section 3.4).
137 |
138 | 2.4. Subsequent Licenses
139 |
140 | No Contributor makes additional grants as a result of Your choice to
141 | distribute the Covered Software under a subsequent version of this
142 | License (see Section 10.2) or under the terms of a Secondary License (if
143 | permitted under the terms of Section 3.3).
144 |
145 | 2.5. Representation
146 |
147 | Each Contributor represents that the Contributor believes its
148 | Contributions are its original creation(s) or it has sufficient rights to
149 | grant the rights to its Contributions conveyed by this License.
150 |
151 | 2.6. Fair Use
152 |
153 | This License is not intended to limit any rights You have under
154 | applicable copyright doctrines of fair use, fair dealing, or other
155 | equivalents.
156 |
157 | 2.7. Conditions
158 |
159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
160 | Section 2.1.
161 |
162 |
163 | 3. Responsibilities
164 |
165 | 3.1. Distribution of Source Form
166 |
167 | All distribution of Covered Software in Source Code Form, including any
168 | Modifications that You create or to which You contribute, must be under
169 | the terms of this License. You must inform recipients that the Source
170 | Code Form of the Covered Software is governed by the terms of this
171 | License, and how they can obtain a copy of this License. You may not
172 | attempt to alter or restrict the recipients' rights in the Source Code
173 | Form.
174 |
175 | 3.2. Distribution of Executable Form
176 |
177 | If You distribute Covered Software in Executable Form then:
178 |
179 | a. such Covered Software must also be made available in Source Code Form,
180 | as described in Section 3.1, and You must inform recipients of the
181 | Executable Form how they can obtain a copy of such Source Code Form by
182 | reasonable means in a timely manner, at a charge no more than the cost
183 | of distribution to the recipient; and
184 |
185 | b. You may distribute such Executable Form under the terms of this
186 | License, or sublicense it under different terms, provided that the
187 | license for the Executable Form does not attempt to limit or alter the
188 | recipients' rights in the Source Code Form under this License.
189 |
190 | 3.3. Distribution of a Larger Work
191 |
192 | You may create and distribute a Larger Work under terms of Your choice,
193 | provided that You also comply with the requirements of this License for
194 | the Covered Software. If the Larger Work is a combination of Covered
195 | Software with a work governed by one or more Secondary Licenses, and the
196 | Covered Software is not Incompatible With Secondary Licenses, this
197 | License permits You to additionally distribute such Covered Software
198 | under the terms of such Secondary License(s), so that the recipient of
199 | the Larger Work may, at their option, further distribute the Covered
200 | Software under the terms of either this License or such Secondary
201 | License(s).
202 |
203 | 3.4. Notices
204 |
205 | You may not remove or alter the substance of any license notices
206 | (including copyright notices, patent notices, disclaimers of warranty, or
207 | limitations of liability) contained within the Source Code Form of the
208 | Covered Software, except that You may alter any license notices to the
209 | extent required to remedy known factual inaccuracies.
210 |
211 | 3.5. Application of Additional Terms
212 |
213 | You may choose to offer, and to charge a fee for, warranty, support,
214 | indemnity or liability obligations to one or more recipients of Covered
215 | Software. However, You may do so only on Your own behalf, and not on
216 | behalf of any Contributor. You must make it absolutely clear that any
217 | such warranty, support, indemnity, or liability obligation is offered by
218 | You alone, and You hereby agree to indemnify every Contributor for any
219 | liability incurred by such Contributor as a result of warranty, support,
220 | indemnity or liability terms You offer. You may include additional
221 | disclaimers of warranty and limitations of liability specific to any
222 | jurisdiction.
223 |
224 | 4. Inability to Comply Due to Statute or Regulation
225 |
226 | If it is impossible for You to comply with any of the terms of this License
227 | with respect to some or all of the Covered Software due to statute,
228 | judicial order, or regulation then You must: (a) comply with the terms of
229 | this License to the maximum extent possible; and (b) describe the
230 | limitations and the code they affect. Such description must be placed in a
231 | text file included with all distributions of the Covered Software under
232 | this License. Except to the extent prohibited by statute or regulation,
233 | such description must be sufficiently detailed for a recipient of ordinary
234 | skill to be able to understand it.
235 |
236 | 5. Termination
237 |
238 | 5.1. The rights granted under this License will terminate automatically if You
239 | fail to comply with any of its terms. However, if You become compliant,
240 | then the rights granted under this License from a particular Contributor
241 | are reinstated (a) provisionally, unless and until such Contributor
242 | explicitly and finally terminates Your grants, and (b) on an ongoing
243 | basis, if such Contributor fails to notify You of the non-compliance by
244 | some reasonable means prior to 60 days after You have come back into
245 | compliance. Moreover, Your grants from a particular Contributor are
246 | reinstated on an ongoing basis if such Contributor notifies You of the
247 | non-compliance by some reasonable means, this is the first time You have
248 | received notice of non-compliance with this License from such
249 | Contributor, and You become compliant prior to 30 days after Your receipt
250 | of the notice.
251 |
252 | 5.2. If You initiate litigation against any entity by asserting a patent
253 | infringement claim (excluding declaratory judgment actions,
254 | counter-claims, and cross-claims) alleging that a Contributor Version
255 | directly or indirectly infringes any patent, then the rights granted to
256 | You by any and all Contributors for the Covered Software under Section
257 | 2.1 of this License shall terminate.
258 |
259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
260 | license agreements (excluding distributors and resellers) which have been
261 | validly granted by You or Your distributors under this License prior to
262 | termination shall survive termination.
263 |
264 | 6. Disclaimer of Warranty
265 |
266 | Covered Software is provided under this License on an "as is" basis,
267 | without warranty of any kind, either expressed, implied, or statutory,
268 | including, without limitation, warranties that the Covered Software is free
269 | of defects, merchantable, fit for a particular purpose or non-infringing.
270 | The entire risk as to the quality and performance of the Covered Software
271 | is with You. Should any Covered Software prove defective in any respect,
272 | You (not any Contributor) assume the cost of any necessary servicing,
273 | repair, or correction. This disclaimer of warranty constitutes an essential
274 | part of this License. No use of any Covered Software is authorized under
275 | this License except under this disclaimer.
276 |
277 | 7. Limitation of Liability
278 |
279 | Under no circumstances and under no legal theory, whether tort (including
280 | negligence), contract, or otherwise, shall any Contributor, or anyone who
281 | distributes Covered Software as permitted above, be liable to You for any
282 | direct, indirect, special, incidental, or consequential damages of any
283 | character including, without limitation, damages for lost profits, loss of
284 | goodwill, work stoppage, computer failure or malfunction, or any and all
285 | other commercial damages or losses, even if such party shall have been
286 | informed of the possibility of such damages. This limitation of liability
287 | shall not apply to liability for death or personal injury resulting from
288 | such party's negligence to the extent applicable law prohibits such
289 | limitation. Some jurisdictions do not allow the exclusion or limitation of
290 | incidental or consequential damages, so this exclusion and limitation may
291 | not apply to You.
292 |
293 | 8. Litigation
294 |
295 | Any litigation relating to this License may be brought only in the courts
296 | of a jurisdiction where the defendant maintains its principal place of
297 | business and such litigation shall be governed by laws of that
298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing
299 | in this Section shall prevent a party's ability to bring cross-claims or
300 | counter-claims.
301 |
302 | 9. Miscellaneous
303 |
304 | This License represents the complete agreement concerning the subject
305 | matter hereof. If any provision of this License is held to be
306 | unenforceable, such provision shall be reformed only to the extent
307 | necessary to make it enforceable. Any law or regulation which provides that
308 | the language of a contract shall be construed against the drafter shall not
309 | be used to construe this License against a Contributor.
310 |
311 |
312 | 10. Versions of the License
313 |
314 | 10.1. New Versions
315 |
316 | Mozilla Foundation is the license steward. Except as provided in Section
317 | 10.3, no one other than the license steward has the right to modify or
318 | publish new versions of this License. Each version will be given a
319 | distinguishing version number.
320 |
321 | 10.2. Effect of New Versions
322 |
323 | You may distribute the Covered Software under the terms of the version
324 | of the License under which You originally received the Covered Software,
325 | or under the terms of any subsequent version published by the license
326 | steward.
327 |
328 | 10.3. Modified Versions
329 |
330 | If you create software not governed by this License, and you want to
331 | create a new license for such software, you may create and use a
332 | modified version of this License if you rename the license and remove
333 | any references to the name of the license steward (except to note that
334 | such modified license differs from this License).
335 |
336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
337 | Licenses If You choose to distribute Source Code Form that is
338 | Incompatible With Secondary Licenses under the terms of this version of
339 | the License, the notice described in Exhibit B of this License must be
340 | attached.
341 |
342 | Exhibit A - Source Code Form License Notice
343 |
344 | This Source Code Form is subject to the
345 | terms of the Mozilla Public License, v.
346 | 2.0. If a copy of the MPL was not
347 | distributed with this file, You can
348 | obtain one at
349 | https://mozilla.org/MPL/2.0/.
350 |
351 | If it is not possible or desirable to put the notice in a particular file,
352 | then You may include the notice in a location (such as a LICENSE file in a
353 | relevant directory) where a recipient would be likely to look for such a
354 | notice.
355 |
356 | You may add additional accurate notices of copyright ownership.
357 |
358 | Exhibit B - "Incompatible With Secondary Licenses" Notice
359 |
360 | This Source Code Form is "Incompatible
361 | With Secondary Licenses", as defined by
362 | the Mozilla Public License, v. 2.0.
363 |
364 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Learn **T**est **D**riven **D**evelopment (**TDD**)
4 |
5 | A brief introduction to **T**est **D**riven **D**evelopment (**TDD**)
6 | in JavaScript for people who want to write _**more reliable code**_.
7 |
8 | [](https://github.com/dwyl/learn-tdd/actions/workflows/ci.yml)
9 | [](https://codecov.io/github/dwyl/learn-tdd?branch=master)
10 | [](https://github.com/dwyl/learn-tdd/blob/main/package.json#L35 "Zero Dependencies")
11 | [](https://github.com/dwyl/learn-tdd/issues)
12 | [](https://hits.dwyl.com/dwyl/learn-tdd)
13 |
14 |
15 |
16 | ## Why?
17 |
18 | _Project(s) without tests_ often end up looking like they are stuck together with _**duct tape**_ ...
19 |
20 | 
21 |
22 | Change _one_ part and _another_ stops working? "_Fixing_" one bug, creates another?
23 |
24 | Wouldn't you *prefer* it if everything was
25 | ***consistent*** and beautifully integrated?
26 | What if _everyone_ on your team worked _like **clock-work**_ in a disciplined order... like a _**Formula 1 Crew**_ ...
27 |
28 | 
29 |
30 | Test Driven Development (TDD) makes your team a well-oiled machine which means you can go _**faster**_.
31 |
32 | Once you have a ***suite*** of tests that run on every change, you will
33 | begin to develop a whole other level of ***confidence*** in your codebase
34 | and will discover a new freedom to be ***creative*** without fear of
35 | "*breaking*" anything unexpectedly; truly *game-changing*.
36 |
37 |
38 | ## What?
39 |
40 | This tutorial will help you get started with
41 | **T**est **D**riven **D**evelopment (**TDD**) *today*!
42 | In the next ***30 minutes*** you will learn _everything_1
43 | you need to write tests for your web project!
44 |
45 | ### Pre-Requisites
46 |
47 | + **A computer** with a web browser
48 | + **Internet access** to download the starter files
49 | + **30 minutes** of your time
50 | + **_Basic_ Programming Skills** (HTML & JavaScript)
51 | + (_**Optional**_) _**Bonus Levels requires**_ you to
52 | [_install_ **Node.js**](https://nodejs.org/download/)
53 |
54 | ### What is Software Testing?
55 | > Software testing is the process of evaluating a software item to detect differences between the expected output and the actual output. Testing assesses the quality of the product. Software testing is a process that should be done during the development process. In other words software testing is a verification and validation process.
56 |
57 |
58 | ### What is TDD?
59 |
60 | > Test-driven development (TDD) is an evolutionary approach to development
61 | which combines test-first development, where you write a test before you write
62 | just enough production code to fulfil that test, and refactoring. In other words,
63 | it’s one way to think through your requirements
64 | or design before you write your functional code.
65 |
66 | *From [Introduction to Test Driven Development (TDD)](https://agiledata.org/essays/tdd.html)*
67 |
68 | #### Further resources
69 | - Software Testing - https://en.wikipedia.org/wiki/Software_testing
70 | - "What is Software Testing" video (from 5:56 onwards) - https://youtu.be/UZy1Dj9JIg4?t=356
71 | - Video intro to Software Development Lifecycle (from 0:52 onwards): https://youtu.be/qMkV_TDdDeA?t=52
72 | - How to Write Clean, Testable Code - https://youtu.be/XcT4yYu_TTs (ignore the Java code focus on the general principles)
73 | + [What is software testing?](https://www.codeproject.com/Tips/351122/What-is-software-testing-What-are-the-different-ty) by _Rehman Zafar_
74 |
75 |
76 | ## How?
77 |
78 | The *first* thing you need to *understand*
79 | is that writing code following TDD (*discipline*)
80 | is a (*slightly*) different approach from simply
81 | diving into solving the problem (*without a test*).
82 |
83 | When reading about TDD you will usually see the expression:
84 | "***Red, Green, Refactor***":
85 |
86 | 
87 |
88 | What this means is that TDD follows a **3-step process**:
89 |
90 | 1. ***Write a Failing Test*** - Understand the (user)
91 | requirements/story well enough to write a test for what you expect.
92 | (_the test should **fail** initially - hence it being "Red"_)
93 |
94 | 2. ***Make the (failing) Test Pass*** - Write (*only*) the code you need
95 | to make the (*failing*) test pass, while ensuring your existing/previous tests
96 | all still pass (*no regressions*).
97 |
98 | 3. ***Refactor the code you wrote*** take the time to tidy up the code
99 | *you* wrote to make it simpler
100 | (*for your future self or colleagues to understand*)
101 | before you need to ship the current feature, do it.
102 |
103 | > Thankfully, because you will have good tests,
104 | you don't _need_ to do any refactoring up-front,
105 | you can always do refactoring _later_
106 | if performance bottlenecks are discovered.
107 | Most programming languages have very efficient compilers/interpreters
108 | that remove much of the need for refactoring.
109 | And if you use a linter your code will be naturally "tidy".
110 |
111 | To develop the *habit(s)* you will need to be successful with TDD
112 | (*and software engineering in general*)
113 | we need to ***write*** a ***test first*** (*and watch it fail*)
114 | and *then* write the code required to make the test pass.
115 |
116 | Writing a _**failing test**_,
117 | before writing the code may seem *counter-intuitive*,
118 | *time consuming* or even "*tedious*" at _**first**_.
119 | But we _urge_ you to think of it this way:
120 |
121 | > The ***test*** is the ***question*** you are asking
122 | > your code is the ***answer*** to the question.
123 | > By having a _clear_ question, you can always check
124 | > that your code is working,
125 | > because it _**consistently**_
126 | > gives you the same answer(s) ... _no surprises_,
127 | even when you're working with a large, inter-dependent code base!
128 |
129 | ## Practical
130 |
131 | > _**Note**: This tutorial is meant to be a beginner-friendly introduction to TDD.
132 | The Vending Machine example is _intentionally_ simple
133 | so you can focus on the principles of testing.
134 | Once you understand the basics,
135 | we encourage you to follow our _complete_ Todo List Tutorial
136 | ([https://github.com/dwyl/**todo-list-javascript-tutorial**](https://github.com/dwyl/todo-list-javascript-tutorial)),
137 | which is a step-by-step guide to building an App
138 | following testing and documentation-first best practices._
139 |
140 | ### Scenario: Vending Machine _Change Calculator_ Micro-Project
141 |
142 | 
143 |
144 | Imagine you are building a **Vending Machine**
145 | that allows people to buy any item it contains.
146 | The machine accepts coins and calculates the change
147 | to be returned to the customer, given the item **price**
148 | and the **cash** received.
149 |
150 | ### Single *File* App
151 |
152 | We can build the _entire_ "project" in a _**single file**_: `index.html`
153 |
154 | > _**Note**: In practice you want to split your JavaScript,
155 | CSS and HTML (Templates) into **separate** files,
156 | but for this example we are keeping everything in `index.html` for simplicity.
157 | If you make it to the "Bonus Levels" you will split things out!_
158 |
159 | Create a directory on your computer called **vending-machine**:
160 |
161 | In your **terminal** type this command:
162 | ```sh
163 | mkdir vending-machine && cd vending-machine
164 | ```
165 | (_This will create the directory and move you into it_)
166 |
167 | Next create a file called **index.html** e.g: `atom index.html`
168 | (which creates and opens the file in the [Atom text editor](https://atom.io/)
169 | if you have it installed)
170 |
171 | (_**Note**: The "atom" command is not installed by default.
172 | In the Atom menu bar there is a command named “Install Shell Commands”
173 | which installs a new command in your Terminal called "atom"._)
174 |
175 | Now copy-paste the following *sample code* into the newly created `index.html` file to get started:
176 |
177 | ```html
178 |
179 |
180 |
181 | Vending Machine Change Calculator TDD Tutorial
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
Vending Machine Change Calculator
190 |
Calculate the change (coins) to return to a customer when they buy something.
191 |
192 |
193 |
194 |
195 |
196 |
197 |
209 |
210 |
211 | ```
212 |
213 | #### Open index.html in your Browser
214 |
215 | When you ***open*** `index.html` in your ***web browser***
216 | you should expect to see something like this: (_without the annotation pointing out the qunit div, and the green and red annotations pointing out the Passing and Failing tests_)
217 |
218 | 
219 |
220 |
221 |
222 | ##### Explanation
223 |
224 | There is quite a lot of code in the **index.html** you just created,
225 | let's step through it to understand the parts:
226 |
227 | The first part of **index.html** is a standard HTML head and body:
228 |
229 | ```html
230 |
231 |
232 |
233 | Vending Machine Change Calculator TDD
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
Vending Machine Change Calculator
242 |
Calculate the Change for a Given Price and Cash Received
243 |
244 | ```
245 | Nothing special here, we are simply setting up the page
246 | and loading the CSS files.
247 |
248 | Next we see the **qunit divs**
249 | (_where the **test results** will be **displayed**_)
250 | and load the JQuery and QUnit Libraries from CDN:
251 |
252 | ```html
253 |
254 |
255 |
256 |
257 | ```
258 |
259 | Finally we see our test(s) - the interesting part of the file:
260 |
261 | ```html
262 |
276 |
277 |
278 | ```
279 |
280 | If you are new to writing ***automated tests***, don't worry -
281 | they are really simple. There are **3 parts**:
282 |
283 | 1. **Description** - usually the *first* parameter to QUnit's test() method, describing what is expected to happen in the test
284 | 2. **Computation** - executes a function/method
285 | (*which invokes the method you will write to make your test pass*)
286 | 3. **Assertion** - verifies that the result of your computation
287 | is what you ***expect*** it to be.
288 |
289 | 
290 |
291 | In the above screenshot, the assertion is `assert.equal(result, 2)`
292 | We are giving the `equal` method two arguments; the `result` of our computation
293 | and our expected value - in this case **2**. _That's it_.
294 |
295 |
296 | _**Note**_:
297 | The latest version of QUnit uses the `QUnit.test()` function to run tests.
298 | Later in this workshop we use [blanket.js](https://blanketjs.org/)
299 | which is not compatible with the latest
300 | version of QUnit. It is for this reason
301 | that we are calling `test()` to run the tests in this workshop.
302 |
303 | ##### Further Reading:
304 |
305 | + Test assertion: https://en.wikipedia.org/wiki/Test_assertion
306 | + What are Test Assertions and how do they work?:
307 | https://www.thoughtworks.com/insights/blog/test-assertions-how-do-they-work
308 |
309 |
310 | ## Requirements
311 |
312 | As a customer, I want to buy a selected item from the **vending machine**
313 | and see what my change is as a **result** into the various **coins**
314 | so that I can select one of the options and receive my change.
315 |
316 | Acceptance criteria:
317 | - A successful call of a function `getChange` should return
318 | the change value in the various **coins** available
319 | - Unit Tests should exist when the function is ready
320 | - The selection of the desired return is out of scope
321 |
322 | ##### Complementary User Story view
323 | > Given a **Price** and an amount of **Cash** from the Customer
324 | > Return: **Change** to the customer (*in notes and coins*).
325 |
326 | ### Understand what is needed
327 |
328 | + Create a `function` called `getChange` that accepts _**two parameters**_:
329 | `totalPayable` and `cashPaid`
330 | + For a given `totalPayable`
331 | (the total amount an item in the vending machine costs)
332 | and `cashPaid` (the amount of cash the customer paid into the vending machine),
333 | `getChange` should _**calculate**_ the _**change**_
334 | the machine should _return_ to the customer
335 | + `getChange` should _**return**_ change as an `array` of coins (largest to smallest)
336 | that the vending machine will need to _dispense_ to the customer.
337 |
338 | #### _Example_
339 |
340 | If a customer buys an item costing £2.15
341 | (_we represent this as **215 pennies**_ `totalPayable`)
342 | and pays £3 (3 x £1 or _**300 pennies**_ `cashPaid`)
343 | into the vending machine, the _**change**_ will be **85p**.
344 | To dispense the 85p of change we should _return_
345 | **four coins** to the person: 50p, 20p, 10p and 5p.
346 | An **array** of these coins would look like: `[50, 20, 10, 5]`
347 |
348 | #### Coins
349 |
350 | In the UK we have the following Coins:
351 |
352 | 
353 |
354 |
355 | If we use the penny as the unit (i.e. 100 pennies in a pound)
356 | the coins can be represented as:
357 |
358 | - 200 (£2)
359 | - 100 (£1)
360 | - 50 (50p)
361 | - 20 (20p)
362 | - 10 (10p)
363 | - 5 (5p)
364 | - 2 (2p)
365 | - 1 (1p)
366 |
367 | this can be stored as an Array:
368 |
369 | ```javascript
370 | var coins = [200, 100, 50, 20, 10, 5, 2, 1];
371 | ```
372 |
373 | _**Note**_: The same can be done for any other cash system ($ ¥ €)
374 | simply use the cent, sen or rin as the unit and scale up notes.
375 |
376 | #### The First Test
377 |
378 | If you are *totally* new to TDD I recommend reading this
379 | [introductory article](https://www.agiledata.org/essays/tdd.html) by Scott Ambler
380 | (especially the diagrams) otherwise this (test-fail-code-pass) process
381 | may seem *strange* ...
382 |
383 | In **T**est **F**irst **D**evelopment (TFD) we write a test *first* and *then*
384 | write the code that makes the test pass.
385 |
386 | #### First Requirement
387 |
388 | So, back in our **index.html** file ***remove the dummy tests***
389 | and add the following lines:
390 |
391 | ```js
392 | test('getChange(1,1) should equal [] - an empty array', function(assert) {
393 | var result = getChange(1, 1); //no change/coins just an empty array
394 | var expected = [];
395 | assert.deepEqual(result, expected);
396 | }); // use deepEqual for arrays see: https://api.qunitjs.com/deepEqual/
397 | ```
398 | We use QUnit's `deepEqual` (_assert_) method to check that all the _elements_
399 | in the two arrays are _**identical**_. see: https://api.qunitjs.com/deepEqual/
400 |
401 | At this point, your `index.html` file should look like this:
402 |
403 | ```html
404 |
405 |
406 |
407 | Vending Machine Change Calculator TDD
408 |
409 |
410 |
411 |
412 |
413 |
Vending Machine Change Calculator
414 |
Calculate the Change for a Given Price and Cash Received
415 |
416 |
417 |
418 |
419 |
420 |
421 |
424 |
425 |
433 |
434 |
435 | ```
436 |
437 |
438 | #### Watch it _Fail_
439 |
440 | Back in your browser window, _refresh_ the browser and watch it *fail*:
441 |
442 | 
443 |
444 | > **Q**: Why *deliberately* write a test we *know* is going to *fail*...?
445 | > **A**: To get used to the idea of *only* writing the code required to *pass*
446 | > the *current* (*failing*) *test*.
447 | > *Read*: "***The Importance of Test Failure***:
448 | https://www.sustainabletdd.com/2012/03/importance-of-test-failure.html
449 | **Note**: This also proves the test **will** fail if the code doesn't behave as expected.
450 |
451 | #### Create the getChange `function`
452 |
453 | In your `index.html` file add the following code (*above the tests*)
454 |
455 | ```js
456 |
464 | ```
465 |
466 | Your `index.html` should now look something like this:
467 |
468 | ```html
469 |
470 |
471 |
472 | Vending Machine Change Calculator TDD
473 |
474 |
475 |
476 |
477 |
478 |
Vending Machine Change Calculator
479 |
Calculate the Change for a Given Price and Cash Received
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
496 |
497 |
505 |
506 |
507 | ```
508 |
509 |
510 | #### Refresh `index.html` in the Browser
511 |
512 |
513 | 
514 |
515 | It Passed!!
516 |
517 | #### Now Let's Write A *Real* Test
518 |
519 | Going back to the requirements, we need our `getChange` method to accept
520 | two arguments/parameters (`totalPayable` and `cashPaid`), and to `return` an
521 | `array` containing the coins equal to the difference between them:
522 |
523 | e.g:
524 | ```js
525 | totalPayable = 215 // £2.15
526 | cashPaid = 300 // £3.00
527 | difference = 85 // 85p
528 | change = [50,20,10,5] // 50p, 20p, 10p, 5p
529 | ```
530 |
531 | Add the following test to tests section of `index.html`:
532 |
533 | ```javascript
534 | test('getChange(215, 300) should return [50, 20, 10, 5]', function(assert) {
535 | var result = getChange(215, 300); // expect an array containing [50,20,10,5]
536 | var expected = [50, 20, 10, 5];
537 | assert.deepEqual(result, expected);
538 | })
539 | ```
540 |
541 | #### Write the Method to Pass the Test
542 |
543 | What if I _**cheat**_ and make `getChange` return the expected result?
544 |
545 | ```javascript
546 | function getChange (totalPayable, cashPaid) {
547 | 'use strict';
548 |
549 | var change = [50, 20, 10, 5]; // just "enough to pass the failing test"
550 |
551 | return change;
552 | };
553 | ```
554 |
555 | This will _pass_ the new test, but it also introduces a regression. The original
556 | test `getChange(1,1) should equal [] - an empty array` is now failing.
557 |
558 | Step 2 of the **TDD** process requires that *all* tests should pass, not just the
559 | newly added one.
560 |
561 | The `getChange` function needs to cater for two scenarios; when change should be returned and when it shouldn't. A new implementation of `getChange` that
562 | handles both scenarios could be:
563 |
564 | ```javascript
565 | function getChange (totalPayable, cashPaid) {
566 | 'use strict';
567 |
568 | var change = [];
569 |
570 | if((cashPaid - totalPayable) != 0) { // Is any change required?
571 | change = [50, 20, 10, 5]; // just "enough to pass the failing test"
572 | }
573 |
574 | return change;
575 | };
576 | ```
577 |
578 | The regression has been fixed and all tests _pass_, but you have *hard coded*
579 | the result (*not exactly useful for a calculator...*)
580 |
581 | This only works *once*. When the Spec (Test) Writer writes the next test,
582 | the method will need to be re-written to satisfy it.
583 |
584 | Let's try it. Work out what you expect so you can write your test:
585 | ```js
586 | totalPayable = 486 // £4.86
587 | cashPaid = 600 // £6.00
588 | difference = 114 // £1.14
589 | change = [100,10,2,2] // £1, 10p, 2p, 2p
590 | ```
591 |
592 | Add the following test to `index.html` and refresh your browser:
593 |
594 | ```javascript
595 | test('getChange(486, 600) should equal [100, 10, 2, 2]', function(assert) {
596 | var result = getChange(486, 600);
597 | var expected = [100, 10, 2, 2];
598 | assert.deepEqual(result, expected);
599 | })
600 | ```
601 |
602 |
603 | #### Should We _Keep Cheating or Solve the Problem_?
604 |
605 | We could _keep cheating_ by writing a series of if statements:
606 |
607 | ```javascript
608 | function getChange (totalPayable, cashPaid) {
609 | 'use strict';
610 |
611 | var change = [];
612 |
613 | if((cashPaid - totalPayable) != 0) { // Is any change required?
614 | if(totalPayable == 486 && cashPaid == 600)
615 | change = [100, 10, 2, 2];
616 | else if(totalPayable == 215 && cashPaid == 300)
617 | change = [50, 20, 10, 5];
618 | }
619 |
620 | return change;
621 | };
622 | ```
623 | The _**Arthur Andersen Approach**_ gets results in the *short run* ...
624 |
625 | But it's arguably *more work* than simply *solving* the problem.
626 | So let's do that instead.
627 |
628 | # Try It Yourself (_before looking at the solution_!)
629 |
630 | > Try to create your own `getChange` method that passes the three tests
631 | > _before_ you look at the solution...
632 |
633 | To re-cap, these are our three tests:
634 | ```js
635 | test('getChange(1,1) should equal [] - an empty array', function(assert) {
636 | var result = getChange(1, 1); //no change/coins just an empty array
637 | var expected = [];
638 | assert.deepEqual(result, expected);
639 | });
640 |
641 | test('getChange(215, 300) should return [50, 20, 10, 5]', function(assert) {
642 | var result = getChange(215, 300); // expect an array containing [50,20,10,5]
643 | var expected = [50, 20, 10, 5];
644 | assert.deepEqual(result, expected);
645 | });
646 |
647 | test('getChange(486, 600) should equal [100, 10, 2, 2]', function(assert) {
648 | var result = getChange(486, 600);
649 | var expected = [100, 10, 2, 2];
650 | assert.deepEqual(result, expected);
651 | });
652 | ```
653 |
654 | #### One More Test to be _Sure_ it Works?
655 |
656 | Let's invent a test that will return one of each of the coins ...
657 |
658 | Recall that we have 8 types of coins:
659 |
660 | ```javascript
661 | var coins = [200, 100, 50, 20, 10, 5, 2, 1];
662 | ```
663 |
664 | The sum of the (_`array` containing one of each_) coins is: **388**p
665 |
666 | So, we need to create a test in which we **pay £4** for an item costing 12p.
667 | (A bit unrealistic, but if it works we know our `getChange` method is _ready_!)
668 |
669 | ```js
670 | test('getChange(12, 400) should return [200, 100, 50, 20, 10, 5, 2, 1]', function(assert) {
671 | var result = getChange(12, 400);
672 | var expected = [200, 100, 50, 20, 10, 5, 2, 1];
673 | assert.deepEqual(result, expected);
674 | });
675 | ```
676 |
677 | When these tests pass, your work is done.
678 |
679 |
680 |
681 |
682 |
683 | ## Solution(s) [](https://github.com/dwyl/learn-tdd/issues)
684 |
685 | _**Note**_: Feel free to suggest a more _compact_ algorithm.
686 |
687 | ### "Imperative" Version ("Two For Loops")
688 |
689 | ```javascript
690 | var coins = [200, 100, 50, 20, 10, 5, 2, 1]
691 | function getChange (payable, paid) {
692 | var change = [];
693 | var length = coins.length;
694 | var remaining = paid - payable; // we reduce this below
695 |
696 | for (var i = 0; i < length; i++) { // loop through array of notes & coins:
697 | var coin = coins[i];
698 |
699 | var times_coin_fits = Math.floor(remaining / coin); // no partial coins
700 | if(times_coin_fits >= 1) { // check coin fits into the remaining amount
701 |
702 | for(var j = 0; j < times_coin_fits ; j++) { // add coin to change x times
703 | change.push(coin);
704 | remaining = remaining - coin; // subtract coin from remaining
705 | }
706 | }
707 | }
708 | return change;
709 | };
710 | ```
711 |
712 | ### "Functional"
713 |
714 | The "functional" solution is more _compact_ than the "nested for loops":
715 |
716 | ```js
717 | const COINS = [200, 100, 50, 20, 10, 5, 2, 1]; // "constant" of all coins
718 | function getChange (payable, paid) {
719 | return COINS.reduce((change, coin) => {
720 | const change_sum = change.reduce((sum, coin) => sum + coin, 0);
721 | const remaining = paid - payable - change_sum;
722 | const times_coin_fits = Math.floor(remaining / coin);
723 | return change.concat(Array(times_coin_fits).fill(coin));
724 | }, []); // change array starts out empty and gets filled iteratively.
725 | }
726 | ```
727 |
728 | Don't panic if you are _unfamiliar_ with the JavaScript
729 | `Array.Map` & `Array.Reduce` methods;
730 | they were new to everyone _once_.
731 |
732 | We recommend reading:
733 |
734 | + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Map
735 | + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
736 | + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
737 | + Example Functional JavaScript: https://www.sitepoint.com/map-reduce-functional-javascript
738 | + [Mostly Adequate Guide To Functional Programming](https://github.com/MostlyAdequate/mostly-adequate-guide)
739 |
740 |
741 |
742 | ### Alternative Solution
743 |
744 | An alternative shared by @blunket:
745 |
746 | ```javascript
747 | var cointypes = [200, 100, 50, 20, 10, 5, 2, 1];
748 | function getChange(price, paid) {
749 | var difference = paid - price;
750 | var change = [];
751 |
752 | cointypes.forEach(function(coin) {
753 | // keep adding the current coin until it's more than the difference
754 | while (difference >= coin) {
755 | change.push(coin);
756 | difference = difference - coin;
757 | }
758 | });
759 |
760 | return change;
761 | }
762 | ```
763 |
764 | If you see this:
765 |
766 | 
767 |
768 | _**Congratulations! You can do Test Driven Development**_ (TDD)!!
769 |
770 | Give yourself a pat on the back! **Tweet your _success_**!
771 | _or **Re-Tweet**_: https://twitter.com/livelifelively/status/768645514120212480
772 | [](https://twitter.com/livelifelively/status/768645514120212480)
773 |
774 | _Take a break, grab some water and come back for the #**BonusLevel**_
775 |
776 |
777 | - - -
778 |
779 |
780 |
781 | ## Bonus Level 1: Code Coverage (10 mins)
782 |
783 | ### What is Code Coverage?
784 |
785 | 
786 |
787 | In computer programming, code coverage is a measure used to describe
788 | the degree to which the source code of a program is tested
789 | by a particular test suite.
790 |
791 | In other words: if there is code in the codebase which is not "_covered_"
792 | by a test, it could potentially be a source of bugs or undesirable behaviour.
793 |
794 | > Read more: https://en.wikipedia.org/wiki/Code_coverage
795 |
796 | ### Example from our Vending Machine _Coin Change_ Example
797 |
798 | Imagine the makers of the Vending Machine (_unknowingly_)
799 | hired a _**rogue**_ programmer to build the change calculator.
800 |
801 | The _**rogue**_ programmer charged below the "_market rate_",
802 | delivered the code quickly and even included tests!
803 |
804 | The makers of the vending machine think that everything is working fine,
805 | all the _tests_ pass and when they try the machine it dispenses the merchandise
806 | and the _correct change every time_.
807 |
808 | But in the `getChange` method the
809 | _**rogue**_ programmer put in the following lines:
810 |
811 | ```js
812 | if(cashPaid == 1337) {
813 | ATM = [20, 10, 5, 2];
814 | for(var i = 0; i< 18; i++) { ATM.push(100) };
815 | return ATM; }
816 | ```
817 |
818 | If all the QA person did was run the tests they would see them
819 | all "green" and think the job was well done.
820 |
821 | But ... once the vending machines had gone into service,
822 | e.g: one in every train station in the country.
823 | The Vending Machine company begins to notice that there is less money
824 | in them than they expect ... They don't understand why because they only
825 | hire _trustworthy_ people to re-stock the machines.
826 |
827 | One day the Vending Machine Company decide to hire _you_
828 | to review the code in the `getChange` calculator
829 | and you discover the _**rogue**_ programmer trick!
830 |
831 | Every time the _**rogue**_ programmer inserts £13.37 into _any_
832 | vending machine it will payout £18.37 i.e: a **£5 payout**
833 | (and a "_free_" item from the vending machine!)
834 |
835 | > _How could this have been **prevented**_?
836 |
837 | The answer is ***code coverage***!
838 |
839 | > _Note: Checking code coverage is **not a substitute for QA/Code Review**..._!
840 |
841 | ### Blanket.js
842 |
843 | To check the _coverage_ of code being executed
844 | (_in the browser_) we use **Blanket.js**
845 |
846 | > See: https://blanketjs.org/ and https://github.com/alex-seville/blanket
847 |
848 | To *run* blanket.js we need to separate our tests and solution
849 | into distinct **.js** files:
850 |
851 | **test.js** contains our unit tests
852 | ```js
853 | test('getChange(1,1) should equal [] - an empty array', function(assert) {
854 | var result = getChange(1, 1); //no change/coins just an empty array
855 | var expected = [];
856 | assert.deepEqual(result, expected);
857 | });
858 |
859 | test('getChange(215, 300) should return [50, 20, 10, 5]', function(assert) {
860 | var result = getChange(215, 300); // expect an array containing [50,20,10,5]
861 | var expected = [50, 20, 10, 5];
862 | assert.deepEqual(result, expected);
863 | });
864 |
865 | test('getChange(486, 600) should equal [100, 10, 2, 2]', function(assert) {
866 | var result = getChange(486, 600);
867 | var expected = [100, 10, 2, 2];
868 | assert.deepEqual(result, expected);
869 | });
870 |
871 | test('getChange(12, 400) should return [200, 100, 50, 20, 10, 5, 2, 1]', function(assert) {
872 | var result = getChange(12, 400);
873 | var expected = [200, 100, 50, 20, 10, 5, 2, 1];
874 | assert.deepEqual(result, expected);
875 | });
876 | ```
877 |
878 | **change.js** has the `getChange` method.
879 | ```js
880 | var coins = [200, 100, 50, 20, 10, 5, 2, 1]
881 | function getChange(payable, paid) {
882 | var change = [];
883 | var length = coins.length;
884 | var remaining = paid - payable; // we reduce this below
885 |
886 | for (var i = 0; i < length; i++) { // loop through array of notes & coins:
887 | var coin = coins[i];
888 |
889 | var times_coin_fits = Math.floor(remaining / coin); // no partial coins
890 | if(times_coin_fits >= 1) { // check coin fits into the remaining amount
891 |
892 | for(var j = 0; j < times_coin_fits; j++) { // add coin to change x times
893 | change.push(coin);
894 | remaining = remaining - coin; // subtract coin from remaining
895 | }
896 | }
897 | }
898 | if(paid == 1337) {
899 | ATM = [20, 10, 5, 2];
900 | for(var i = 0; i< 18; i++) { ATM.push(100) };
901 | return ATM;
902 | }
903 | else {
904 | return change;
905 | }
906 | };
907 | ```
908 | Include these two files _and_ the **Blanket.js** library in your index.html:
909 | ```html
910 |
911 |
912 |
913 |
914 | ```
915 |
916 | ### _Live_ Server
917 |
918 | > _**Note**: This is a _light_ taste of Node.js for absolute beginners._
919 |
920 | Because we are loading external **.js** files, our web browser will not _allow_
921 | us to simply open the **index.html** from the directory.
922 |
923 | Open your terminal and run this command
924 | to _**install** the **node modules** and **start** the **live server**_:
925 |
926 | ```sh
927 | npm init -f && npm install live-server --save-dev && node_modules/.bin/live-server --port=8000
928 | ```
929 |
930 | It will take a minute to install,
931 | but once that's done your `live-server` will start up.
932 |
933 | That starts a node.js HTTP server on port 8000.
934 |
935 | > Visit: http://localhost:8000/?coverage in your web browser
936 |
937 | You should expect to see:
938 |
939 | 
940 |
941 | (Make sure to tick "Enable Coverage", as it is not checked by default!)
942 |
943 | ### Click line #1 in the Blanket.js section to expand the code coverage view
944 |
945 | 
946 |
947 | Here we can clearly see which lines are **not** being covered by the tests!
948 | We can quickly identify a potential for bugs or _rogue_ code and remove it!
949 |
950 | #### Hold on ... What if the _rogue_ code is all on _one line_?
951 |
952 |
953 |
954 | 
955 |
956 | > The (_sad?_) _fact_ is:
957 | > Blanket.js Code Coverage analysis will not detect _all_ bugs or rogue code.
958 | > you **still need** a _**human**_ to do a _**code review**_!
959 |
960 | _But_ ... if you use _**Istanbul**_ to check coverage on the server, you'll see that only part of the single line of _rogue_ code was executed.
961 | Istanbul is _much_ better at spotting un-tested code!
962 |
963 | > We wrote a **beginners guide** to **Code Coverage with Istanbul**:
964 | [https://github.com/dwyl/**learn-istanbul**](https://github.com/dwyl/learn-istanbul)
965 | that goes into _detail_.
966 |
967 | ## Bonus Level 2: Node.js (_server-side_) Tests (10 mins)
968 |
969 | > _**Note**: You will need to have Node.js installed on your machine
970 | for this section.
971 | > If you don't already have it, download it from:
972 | https://nodejs.org/en/download/_
973 |
974 | The beauty of writing JavaScript is that you can _**run**_ it _**anywhere**_!
975 |
976 | In this bonus level we are going
977 | to run our tests _**"server-side"**_ using **Node.js**.
978 |
979 | Add these lines to the top of the **test.js** file you
980 | created in **Bonus Level 1**
981 |
982 | ```js
983 | /* The code block below ONLY Applies to Node.js - This Demonstrates
984 | re-useability of JS code in both Back-end and Front-end! #isomorphic */
985 | /* istanbul ignore if */
986 | if (typeof module !== 'undefined' && module.exports) {
987 | var QUnit = require('qunitjs'); // require QUnit node.js module
988 | // alias the QUnit.test method so we don't have to change all our tests
989 | var test = QUnit.test; // stores a copy of QUnit.test
990 | require('qunit-tap')(QUnit, console.log); // use console.log for test output
991 | var getChange = require('./change.js'); // load our getChange method
992 | }
993 | ```
994 |
995 | And add these lines to the bottom of the **test.js** file
996 | you created in **Bonus Level 1**
997 |
998 | ```js
999 | /* istanbul ignore next */
1000 | if (typeof module !== 'undefined' && module.exports) { QUnit.load(); } // run the tests
1001 | ```
1002 |
1003 | In addition, you need to add this to the **change.js** file you
1004 | created in **Bonus Level 1**
1005 |
1006 | ```js
1007 | /* The code block below ONLY Applies to Node.js - This Demonstrates
1008 | re-useability of JS code in both Back-end and Front-end! #isomorphic */
1009 | /* istanbul ignore next */
1010 | if (typeof module !== 'undefined' && module.exports) {
1011 | module.exports = getChange; // allows CommonJS/Node.js require()
1012 | }
1013 | ```
1014 |
1015 | Next, install the following node.js modules by running `npm install qunitjs qunit-tap istanbul --save-dev`:
1016 | + QUnit node.js module
1017 | + qunit-tap (for command line output)
1018 | + Istanbul for server-side code coverage
1019 |
1020 | Run the tests in your _terminal_:
1021 | ```sh
1022 | node test.js
1023 | ```
1024 |
1025 | And run Istanbul to see the server-side code coverage:
1026 | ```sh
1027 | ./node_modules/.bin/istanbul cover test.js
1028 | ```
1029 | At this point, you should see something like this in your terminal:
1030 |
1031 | 
1032 |
1033 | Execute `open ./coverage/lcov-report/index.html` to view the detailed coverage report, and you should see something like this:
1034 |
1035 | 
1036 |
1037 | This clearly highlights the "*rogue*" code from the previous **Bonus Level**.
1038 |
1039 | Let's _remove_ the "_rogue_" code lines and re-run the tests:
1040 |
1041 | 
1042 |
1043 | Refresh the Code Coverage report in your browser:
1044 |
1045 | 
1046 |
1047 | > _**Boom**_! Now you know how to run your QUnit-based Unit Tests server-side!
1048 |
1049 |
1050 | ### *Top Tip*: Use [Codecov.io](https://codecov.io/#features) to Track Coverage in your Projects!
1051 |
1052 | > Now that you understand how Code Coverage Works,
1053 | > you can use https://codecov.io/#features
1054 | to ***track*** Coverage in your project over time!
1055 | > You can even add a [***Badge***](https://github.com/dwyl/repo-badges)
1056 | to your readme file e.g:
1057 | [](https://codecov.io/github/dwyl/learn-tdd?branch=master)
1058 | to show others that you *care* about testing.
1059 |
1060 | ## Bonus Level 3: _Continuous Integration_ (5 mins)
1061 |
1062 | If you are new to **Continuous Integration** (**CI** _in general_)
1063 | or **Travis CI** check out our tutorial:
1064 | https://github.com/docdis/learn-travis
1065 |
1066 | To quickly add CI support to your project:
1067 |
1068 | **1**) **Visit**: https://travis-ci.org/profile and **Login**
1069 | with your **GitHub account**
1070 | **2**) Enable Travis for your project
1071 | (_**Note**_: The project will need to be hosted on GitHub)
1072 |
1073 | 
1074 |
1075 | **3**) Add a **.travis.yml** file to your project's root directory
1076 | and include the following lines in it:
1077 |
1078 | ```sh
1079 | language: node_js
1080 | node_js:
1081 | - "node"
1082 | ```
1083 | **4**) Ensure that you have a **package.json** file
1084 | with **test** script.
1085 | (_if in doubt, just copy-paste the **package.json** from this project!_)
1086 |
1087 | **5**) **Commit** your changes and **push** them to GitHub
1088 | **6**) Visit the page on Travis-CI for your project. e.g: https://travis-ci.org/dwyl/learn-tdd
1089 | to see the build results.
1090 |
1091 | 
1092 |
1093 | 
1094 |
1095 | Done. [](https://travis-ci.org/dwyl/learn-tdd)
1096 |
1097 |
1098 |
1099 | ## Bonus Level 4: _Documentation_ with [JSDoc](https://github.com/jsdoc3/jsdoc) (5 mins)
1100 |
1101 | > **Note**: Bonus Level 4 ***requires node.js*** to be *installed* on your machine.
1102 | > If you don't already have it installed, don't panic. You don't need to know
1103 | > *anything* about Node.js to work through the examples.
1104 | To download, visit: https://nodejs.org/en/download/
1105 | and install the version for your Operating System.
1106 |
1107 | If you took a peak at the solution in **change.js** you may have noticed
1108 | that there is a ***comment block*** at the top of the file:
1109 |
1110 | ```js
1111 | /**
1112 | * getChange accepts two parameters (totalPayable and cashPaid) and calculates
1113 | * the change in "coins" that needs to be returned.
1114 | * @param {number} totalPayable the integer amount (in pennies) to be paid
1115 | * @param {number} cashPaid the integer amount (in pennies) the person paid
1116 | * @returns {array} list of coins we need to dispense to the person as change
1117 | * @example
1118 | * getChange(215, 300); // returns [50, 20, 10, 5]
1119 | */
1120 | ```
1121 | This is a JSDoc comment block which documents the `getChange` function/method.
1122 |
1123 | The beauty of writing documenting comments this way is that you can easily
1124 | produce documentation for your project in 3 easy steps:
1125 |
1126 | **1**) Install jsdoc: in your terminal run the following command
1127 | `npm install jsdoc --save-dev`
1128 |
1129 | **2**) Run the `jsdoc` command in your terminal:
1130 | `./node_modules/.bin/jsdoc change.js`
1131 |
1132 | **3**) Open the resulting **html** file
1133 | `open ./out/global.html#getChange`
1134 | and you should see something like this in your web browser:
1135 |
1136 | 
1137 |
1138 | This _clearly_ documents the functionality of the `getChange` method.
1139 |
1140 | - - -
1141 |
1142 | ## _Conclusion_
1143 |
1144 | In the last **90 minutes** you _**learned how**_ to:
1145 | + Write code following **T**est **D**riven **D**evelopment (**TDD**) discipline
1146 | + Generate and view the **code coverage** for both front-end and back-end JavaScript Code
1147 | + Set up **Travis-CI Continuous Integration** for your project
1148 | (so that you can keep track of the test/build status for your project)
1149 | + Use **JSDoc** to generate documentation for your code after writing simple comment blocks
1150 | above your functions.
1151 |
1152 | > _Please **Star**_ this repository
1153 | and share it with your coder friends/colleagues.
1154 | > _Help us_ spread the TDD Love by ***re-tweeting***:
1155 | https://twitter.com/dwyl/status/621353373019865089
1156 | > If you have _**any questions**_ please ask:
1157 | > https://github.com/dwyl/learn-tdd/issues
1158 |
1159 | - - -
1160 |
1161 | 1Ok, its not *really* possible to learn "everything" in 30 mins...
1162 | but you'll certainly know *most* of what you need!
1163 | And, if you have *any questions*, _**please ask**_ at:
1164 | https://github.com/dwyl/learn-tdd/issues
1165 |
1166 |
1167 |
1168 | # What (_To Learn_) _Next_?
1169 |
1170 | Now that you know TDD basics, what should you learn/practice _next_...?
1171 |
1172 | + Learn ***Elm Architecture*** to build web applications
1173 | using the _**simple, reliable** and **fast**_ architecture
1174 | with our step-by-step guide:
1175 | [github.com/dwyl/**learn-elm-architecture**-in-javascript](https://github.com/dwyl/learn-elm-architecture-in-javascript)
1176 | This is relevant to anyone who wants to build Web or Mobile Apps using React.js
1177 | (_learning the principles of the **Elm Architecture**
1178 | will help to keep your code well-organised and with a logical rendering flow_)
1179 | + Learn ***Tape*** (_the simplest Node/Browser testing framework_):
1180 | https://github.com/dwyl/learn-tape
1181 | Apply your TDD knowledge to Node.js and browser testing
1182 | using the Tape framework which is both fast and flexible!
1183 | + Learn how to build a Todo List App (TodoMVC) in JavaScript from scratch:
1184 | https://github.com/dwyl/todo-list-javascript-tutorial
1185 | This is the _best_ way to practice your TDD skills by building a _real_ App
1186 | following TDD best-practice from start to finish.
1187 | This is also an extended example of using "Document Driven Development"
1188 | where all code is documented _before_ it is written using JSDoc comments.
1189 |
1190 | # Interested in Contributing?
1191 | _**Please read** our_
1192 | [**contribution guide**](https://github.com/dwyl/contributing)
1193 | (_thank you_!)
1194 |
--------------------------------------------------------------------------------