|
| 1 | +<!doctype html> |
| 2 | +<html class="no-js" lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="utf-8"> |
| 5 | + |
| 6 | + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| 7 | + <title>Get your Frontend JavaScript Code Covered | Code | Nicolas Perriault</title> |
| 8 | + <meta name="description" content="Nicolas Perriault's homepage."> |
| 9 | + <meta name="viewport" content="width=device-width"> |
| 10 | + <link href="//fonts.googleapis.com/css?family=Asap:400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css"> |
| 11 | + <link rel="stylesheet" type="text/css" href="/static/packed.css?1412806084"> |
| 12 | + <link rel="alternate" type="application/rss+xml" href="/code/feed/" title="Code (RSS)"> |
| 13 | + <link rel="alternate" type="application/rss+xml" href="/photography/feed/" title="Photography (RSS)"> |
| 14 | + <link rel="alternate" type="application/rss+xml" href="/talks/feed/" title="Talks (RSS)"> |
| 15 | + <link rel="alternate" type="application/rss+xml" href="/carnet/feed/" title="Carnet (RSS)"> |
| 16 | + <link rel="alternate" type="application/rss+xml" href="/feed/" title="Everything (RSS)"> |
| 17 | + <!--[if lt IE 9]> |
| 18 | + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> |
| 19 | + <![endif]--> |
| 20 | +</head> |
| 21 | +<body class="code " onload="prettyPrint()"> |
| 22 | + <!--[if lt IE 7]> |
| 23 | + <p class="chromeframe">Your browser is <em>ancient!</em> Please <a href="http://www.quirksmode.org/upgrade.html">upgrade</a>.</p> |
| 24 | + <![endif]--> |
| 25 | + <div class="container"> |
| 26 | + <header class="main-title"> |
| 27 | + <h1><a href="/">Hi, I'm <strong>Nicolas.</strong></a></h1> |
| 28 | + <small>I code stuff. I take photos. I write rants.</small> |
| 29 | + </header> |
| 30 | + <main class="contents" role="main"> |
| 31 | + |
| 32 | + |
| 33 | + <article lang="en" class="code" itemscope itemtype="http://schema.org/BlogPosting"> |
| 34 | + <link itemprop="url" href="/code/2013/get-your-frontend-javascript-code-covered/"> |
| 35 | + <header> |
| 36 | + <h2><a itemprop="name" href="/code/2013/get-your-frontend-javascript-code-covered/">Get your Frontend JavaScript Code Covered</a></h2></header> |
| 37 | + |
| 38 | + <section> |
| 39 | + <p><strong>So finally you're <a href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/">testing your frontend JavaScript code</a>? Great! The more you |
| 40 | +write tests, the more confident you are with your code… but how much precisely? |
| 41 | +That's where <a href="http://en.wikipedia.org/wiki/Code_coverage">code coverage</a> might |
| 42 | +help.</strong></p> |
| 43 | +<p>The idea behind code coverage is to record which parts of your code (functions, |
| 44 | +statements, conditionals and so on) have been executed by your test suite, to |
| 45 | +compute metrics out of these data and usually to provide tools for navigating |
| 46 | +and inspecting them.</p> |
| 47 | +<p>Not a lot of frontend developers I know actually test their frontend code, and I |
| 48 | +can barely imagine how many of them have ever setup code coverage… Mostly |
| 49 | +because there are not many frontend-oriented tools in this area I guess.</p> |
| 50 | +<p>Actually I've only found one which provides an adapter for <a href="http://visionmedia.github.io/mocha/">Mocha</a> and actually |
| 51 | +works…</p> |
| 52 | +<blockquote class="twitter-tweet tw-align-center"> |
| 53 | + <p> |
| 54 | + Drinking game for web devs: <br> |
| 55 | + (1) Think of a noun<br> |
| 56 | + (2) Google "<noun>.js"<br> |
| 57 | + (3) If a library with that name exists - drink |
| 58 | + </p> |
| 59 | + — Shay Friedman (@ironshay) |
| 60 | + <a href="https://twitter.com/ironshay/statuses/370525864523743232">August 22, 2013</a> |
| 61 | +</blockquote> |
| 62 | + |
| 63 | +<p><strong><a href="http://blanketjs.org/">Blanket.js</a></strong> is an <em>easy to install, easy to configure, |
| 64 | +and easy to use JavaScript code coverage library that works both in-browser and |
| 65 | +with nodejs.</em></p> |
| 66 | +<p>Its use is dead easy, adding Blanket support to your Mocha test suite is just |
| 67 | +matter of adding this simple line to your HTML test file:</p> |
| 68 | +<pre><code><script src="vendor/blanket.js" |
| 69 | + data-cover-adapter="vendor/mocha-blanket.js"></script> |
| 70 | +</code></pre> |
| 71 | +<p>Source files: <a href="https://raw.github.com/alex-seville/blanket/master/dist/qunit/blanket.min.js">blanket.js</a>, |
| 72 | + <a href="https://raw.github.com/alex-seville/blanket/master/src/adapters/mocha-blanket.js">mocha-blanket.js</a></p> |
| 73 | +<p>As an example, let's reuse the silly <code>Cow</code> example we used <a href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/">in a previous episode</a>:</p> |
| 74 | +<pre><code>// cow.js |
| 75 | +(function(exports) { |
| 76 | + "use strict"; |
| 77 | + |
| 78 | + function Cow(name) { |
| 79 | + this.name = name || "Anon cow"; |
| 80 | + } |
| 81 | + exports.Cow = Cow; |
| 82 | + |
| 83 | + Cow.prototype = { |
| 84 | + greets: function(target) { |
| 85 | + if (!target) |
| 86 | + throw new Error("missing target"); |
| 87 | + return this.name + " greets " + target; |
| 88 | + } |
| 89 | + }; |
| 90 | +})(this); |
| 91 | +</code></pre> |
| 92 | +<p>And its test suite, powered by Mocha and <a href="http://chaijs.com/">Chai</a>:</p> |
| 93 | +<pre><code>var expect = chai.expect; |
| 94 | + |
| 95 | +describe("Cow", function() { |
| 96 | + describe("constructor", function() { |
| 97 | + it("should have a default name", function() { |
| 98 | + var cow = new Cow(); |
| 99 | + expect(cow.name).to.equal("Anon cow"); |
| 100 | + }); |
| 101 | + |
| 102 | + it("should set cow's name if provided", function() { |
| 103 | + var cow = new Cow("Kate"); |
| 104 | + expect(cow.name).to.equal("Kate"); |
| 105 | + }); |
| 106 | + }); |
| 107 | + |
| 108 | + describe("#greets", function() { |
| 109 | + it("should greet passed target", function() { |
| 110 | + var greetings = (new Cow("Kate")).greets("Baby"); |
| 111 | + expect(greetings).to.equal("Kate greets Baby"); |
| 112 | + }); |
| 113 | + }); |
| 114 | +}); |
| 115 | +</code></pre> |
| 116 | +<p>Let's create the HTML test file for it, featuring Blanket and its adapter for |
| 117 | +Mocha:</p> |
| 118 | +<pre><code><!DOCTYPE html> |
| 119 | +<html> |
| 120 | +<head> |
| 121 | + <meta charset="utf-8"> |
| 122 | + <title>Test</title> |
| 123 | + <link rel="stylesheet" media="all" href="vendor/mocha.css"> |
| 124 | +</head> |
| 125 | +<body> |
| 126 | + <div id="mocha"></div> |
| 127 | + <div id="messages"></div> |
| 128 | + <div id="fixtures"></div> |
| 129 | + <script src="vendor/mocha.js"></script> |
| 130 | + <script src="vendor/chai.js"></script> |
| 131 | + <script src="vendor/blanket.js" |
| 132 | + data-cover-adapter="vendor/mocha-blanket.js"></script> |
| 133 | + <script>mocha.setup('bdd');</script> |
| 134 | + <script src="cow.js" data-cover></script> |
| 135 | + <script src="cow_test.js"></script> |
| 136 | + <script>mocha.run();</script> |
| 137 | +</body> |
| 138 | +</html> |
| 139 | +</code></pre> |
| 140 | +<p><strong>Notes</strong>:</p> |
| 141 | +<ul> |
| 142 | +<li>Notice the <code>data-cover</code> attribute we added to the script tag loading the |
| 143 | + source of our library;</li> |
| 144 | +<li>The HTML test file <em>must</em> be served over HTTP for the adapter to be loaded.</li> |
| 145 | +</ul> |
| 146 | +<p>Running the tests now gives us something like this:</p> |
| 147 | +<p><img alt="screenshot" src="/static/code/2013/blanket-coverage.png" /></p> |
| 148 | +<p>As you can see, the report at the bottom highlights that we haven't actually |
| 149 | +tested the case where an error is raised in case a target name is missing. |
| 150 | +We've been informed of that, nothing more, nothing less. We simply know we're |
| 151 | +missing a test here. Isn't this cool? I think so!</p> |
| 152 | +<p>Just remember that code coverage will only <a href="http://codebetter.com/karlseguin/2008/12/09/code-coverage-use-it-wisely/">bring you numbers</a> and raw |
| 153 | +information, not actual proofs that the whole of your <em>code logic</em> has been |
| 154 | +actually covered. If you ask me, the best inputs you can get about your code |
| 155 | +logic and implementation ever are the ones issued out of <a href="http://www.extremeprogramming.org/rules/pair.html">pair programming</a> |
| 156 | +sessions and <a href="http://alexgaynor.net/2013/sep/26/effective-code-review/">code reviews</a> — but that's another story.</p> |
| 157 | +<p><strong>So is code coverage silver bullet? No. Is it useful? Definitely. Happy testing!</strong></p> |
| 158 | + </section> |
| 159 | + <aside> |
| 160 | + <p> |
| 161 | + <span class="article-author" itemprop="author" itemscope itemtype="http://schema.org/Person"> |
| 162 | + <span itemprop="name">Nicolas Perriault</span> — |
| 163 | + </span> |
| 164 | + <time datetime="2013-09-29" itemprop="datePublished">2013-09-29</time> |
| 165 | + — in <a href="/code/" itemprop="genre">Code</a> |
| 166 | + — <a href="/code/2013/get-your-frontend-javascript-code-covered/">Permalink</a> |
| 167 | + — <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">License</a> |
| 168 | + |
| 169 | + — <a href="http://flattr.com/submit/auto?url=https://nicolas.perriault.net/code/2013/get-your-frontend-javascript-code-covered/&title=Get your Frontend JavaScript Code Covered&user_id=n1k0&category=software&language=en">flattr this</a> |
| 170 | + </p> |
| 171 | + </aside> |
| 172 | + <hr> |
| 173 | + <nav> |
| 174 | + <a class="prev" href="/code/2013/functional-javascript-for-crawling-the-web/">Functional JavaScript for crawling the Web</a> |
| 175 | + | |
| 176 | + <a class="next" href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/">Testing your frontend JavaScript code using mocha, chai, and sinon</a> |
| 177 | + </nav> |
| 178 | +</article> |
| 179 | + |
| 180 | + |
| 181 | + </main> |
| 182 | + <nav class="sidebar"> |
| 183 | + <ul> |
| 184 | + <li class="home"><a href="/" hreflang="en">Home</a></li> |
| 185 | + <li class="code"><a href="/code/" hreflang="en">Code</a></li> |
| 186 | + <li class="photography"><a href="/photography/" hreflang="en">Photography</a></li> |
| 187 | + <li class="talks"><a href="/talks/" hreflang="en">Talks</a></li> |
| 188 | + <li class="carnet"><a href="/carnet/" hreflang="fr">Carnet <span>fr</span></a></li> |
| 189 | + <li class="contact"><a href="/contact/" hreflang="en">Contact</a></li> |
| 190 | + </ul> |
| 191 | + </nav> |
| 192 | + <footer class="site-footer"> |
| 193 | + <p> |
| 194 | + © 2012 Nicolas Perriault |
| 195 | + — <a href="https://twitter.com/n1k0">Tweet at me</a> |
| 196 | + — <a href="https://github.com/n1k0">Get my code</a> |
| 197 | + — <a href="http://500px.com/n1k0">Enjoy my pics</a> |
| 198 | + — <a href="/contact/">Contact me</a> |
| 199 | + </p> |
| 200 | + </footer> |
| 201 | + </div> |
| 202 | + <!-- /container --> |
| 203 | + <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> |
| 204 | + <script>window.jQuery || document.write('<script src="js/libs/jquery-1.7.1.min.js"><\/script>')</script> |
| 205 | + <script type="text/javascript" src="/static/js/libs/prettify/prettify.js"></script> |
| 206 | + <script type="text/javascript" src="/static/js/app.js"></script> |
| 207 | + <script src="//platform.twitter.com/widgets.js" charset="utf-8"></script> |
| 208 | +</body> |
| 209 | +</html> |
0 commit comments