1<!DOCTYPE html><html lang="en" class="h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="canonical" href="https://silvenon.com/blog/mocking-with-jest/functions"/><meta property="og:url" content="https://silvenon.com/blog/mocking-with-jest/functions"/><meta property="og:site_name" content="Matija MarohniÄ"/><meta name="twitter:card" content="summary"/><meta name="twitter:site" content="@silvenon"/><meta property="og:type" content="article"/><title>Mocking with Jest: Spying on Functions and Changing Implementation</title><meta property="og:title" content="Mocking with Jest: Spying on Functions and Changing Implementation"/><meta name="twitter:title" content="Mocking with Jest: Spying on Functions and Changing Implementation"/><meta name="description" content="Improve your unit testing experience by using Jest to record calls of particular methods and even changing their implementation as needed."/><meta property="og:description" content="Improve your unit testing experience by using Jest to record calls of particular methods and even changing their implementation as needed."/><meta name="twitter:description" content="Improve your unit testing experience by using Jest to record calls of particular methods and even changing their implementation as needed."/><meta property="article:author" content="Matija MarohniÄ"/><meta property="article:published_time" content="2019-05-21"/><meta property="article:modified_time" content="2022-05-22"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" href="/apple-touch-icon.png"/><link rel="manifest" href="/manifest.webmanifest"/><link rel="alternate" type="application/rss+xml" href="/feed.rss" title="RSS Feed"/><link rel="alternate" type="application/atom+xml" href="/feed.atom" title="Atom Feed"/><meta name="color-scheme" content="light dark"/><meta name="twitter:widgets:theme" content="light"/><link rel="modulepreload" href="/assets/entry.client-BxZlRFGA.js"/><link rel="modulepreload" href="/assets/chunk-UIGDSWPH-C1TJybnd.js"/><link rel="modulepreload" href="/assets/jsx-runtime-DlSKYi2Z.js"/><link rel="modulepreload" href="/assets/chunk-C4jv1B1e.js"/><link rel="modulepreload" href="/assets/root-Ub0h2PaT.js"/><link rel="modulepreload" href="/assets/cloudinary-DLG82wBp.js"/><link rel="modulepreload" href="/assets/dark-mode-Cu1p3Sgb.js"/><link rel="modulepreload" href="/assets/post-series-DCWdy_vg.js"/><link rel="modulepreload" href="/assets/Post-Bje60ncL.js"/><link rel="modulepreload" href="/assets/seo-CgzHf-Sn.js"/><link rel="stylesheet" href="/assets/tailwind-CP90SqpU.css"/>
1<script> 2 ;(function () { 3 const darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches 4 document.documentElement.classList.toggle('dark', darkMode) 5 document 6 .querySelector('meta[name="color-scheme"]') 7 .setAttribute('content', darkMode ? 'dark light' : 'light dark') 8 document 9 .querySelector('meta[name="twitter:widgets:theme"]') 10 .setAttribute('content', darkMode ? 'dark' : 'light') 11 })() 12 </script>
12</head><body class="bg-page dark:bg-page-dark h-full text-black selection:bg-amber-300 selection:text-black dark:text-white"><header id="top" class="px-page print:hidden"><div class="flex items-center px-page prose prose-sm prose-zinc dark:prose-invert sm:prose-base lg:prose-lg xl:prose-xl 2xl:prose-2xl mx-auto prose-a:text-purple-700 prose-a:hover:text-amber-600 prose-figure:text-center prose-code:before:hidden prose-code:after:hidden prose-p:first-of-type:before:hidden prose-p:last-of-type:after:hidden dark:prose-a:text-purple-300 dark:prose-a:hover:text-amber-400 print:max-w-[90ch] print:px-0"><div class="flex-1"><nav><a href="/" data-discover="true">â Home</a></nav></div><div class="py-3"><a href="/" data-discover="true"><svg class="size-7 rounded-sm lg:size-10 lg:rounded-lg" role="img" aria-labelledby="_R_16l_-title" viewBox="0 0 64 64"><title id="_R_16l_-title">logo</title><clipPath id="_R_16l_-top-left"><path d="M64,0.025l-64,64l0,-64l64,0Z"></path></clipPath><clipPath id="_R_16l_-bottom-right"><path d="M0,64l64,-64l0,64l-64,0Z"></path></clipPath><g clip-path="url(#_R_16l_-top-left)"><rect class="fill-current text-purple-500" x="0.025" y="0.025" width="64" height="64"></rect><path class="fill-current text-white" d="M31.989,22.518c-0.031,-4.597 -0.902,-8.134 -2.612,-10.613c-1.71,-2.478 -4.165,-3.717 -7.365,-3.717c-3.2,0 -5.655,1.251 -7.365,3.753c-1.71,2.502 -2.565,6.114 -2.565,10.836l0,6.471c0.063,4.549 0.953,8.043 2.671,10.483c1.718,2.439 4.153,3.659 7.306,3.659c3.185,0 5.636,-1.255 7.354,-3.765c1.717,-2.51 2.576,-6.134 2.576,-10.871l0,-6.236Zm-6.682,7.647c-0.032,2.605 -0.31,4.538 -0.836,5.801c-0.525,1.263 -1.329,1.894 -2.412,1.894c-1.145,0 -1.988,-0.675 -2.529,-2.024c-0.542,-1.349 -0.812,-3.404 -0.812,-6.165l0,-8.541c0.078,-4.942 1.176,-7.413 3.294,-7.413c1.13,0 1.961,0.675 2.494,2.024c0.534,1.349 0.801,3.373 0.801,6.071l0,8.353Z"></path></g><g clip-path="url(#_R_16l_-bottom-right)"><rect class="fill-current text-purple-900 dark:text-purple-800" x="0.025" y="0.025" width="64" height="64"></rect><path class="fill-current text-white" d="M44.945,55.261l-6.553,0l0,-25.659l-6.392,2.469l0,-5.446l12.253,-5.007l0.692,0l0,33.643Z"></path></g></svg></a></div><div class="flex flex-1 items-center justify-end"><form class="flex items-center space-x-2" data-discover="true" action="/dark-mode?redirectTo=%2Fblog%2Fmocking-with-jest%2Ffunctions" method="post"><button id="_R_t6lH1_-switch" role="switch" value="true" aria-checked="false" type="submit" class="relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent bg-gray-200 transition-colors duration-200 ease-in-out focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 focus:outline-hidden dark:bg-purple-400 dark:focus:ring-purple-400 dark:focus:ring-offset-gray-900" name="darkMode"><span id="_R_t6lH1_-switch-label" class="sr-only">Enable dark mode</span><span class="pointer-events-none relative inline-block h-5 w-5 translate-x-0 rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out dark:translate-x-5"><span class="absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-200 ease-out dark:opacity-0 dark:duration-100 dark:ease-out" aria-hidden="true"><svg class="h-3 w-3 bg-white text-gray-400"><use href="/assets/sprite-CXh9QG8C.svg#sun"></use></svg></span><span class="absolute inset-0 flex h-full w-full items-center justify-center opacity-0 transition-opacity duration-100 ease-out dark:opacity-100 dark:duration-200 dark:ease-in" aria-hidden="true"><svg class="h-3 w-3 bg-white text-purple-500"><use href="/assets/sprite-CXh9QG8C.svg#moon"></use></svg></span></span></button>
12<script> 13 ;(function () { 14 const darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches 15 const switchEl = document.getElementById('_R_t6lH1_-switch') 16 const switchLabelEl = document.getElementById('_R_t6lH1_-switch-label') 17 switchEl.setAttribute('value', String(!darkMode)) 18 switchEl.setAttribute('aria-checked', String(darkMode)) 19 switchLabelEl.textContent = darkMode ? 'Disable dark mode' : 'Enable dark mode' 20 })() 21 </script>
21</form></div></div></header><div class="space-y-4 px-page prose prose-sm prose-zinc dark:prose-invert sm:prose-base lg:prose-lg xl:prose-xl 2xl:prose-2xl mx-auto prose-a:text-purple-700 prose-a:hover:text-amber-600 prose-figure:text-center prose-code:before:hidden prose-code:after:hidden prose-p:first-of-type:before:hidden prose-p:last-of-type:after:hidden dark:prose-a:text-purple-300 dark:prose-a:hover:text-amber-400 print:max-w-[90ch] print:px-0"><main><article><h1 class="space-y-2 text-center text-balance lg:space-y-4"><div>Mocking with Jest<span class="sr-only">:</span></div><div class="text-[0.8em] font-normal dark:font-light">Spying on Functions and Changing Implementation</div></h1><span class="sr-only">Published on </span><time class="flex items-center space-x-2 text-gray-500 dark:text-gray-400" dateTime="2019-05-21"><svg aria-hidden="true" class="h-5 w-5"><use href="/assets/sprite-CXh9QG8C.svg#calendar"></use></svg><span>May 21, 2019</span></time><span class="inline-flex items-center space-x-2 text-gray-500 dark:text-gray-400"><svg aria-hidden="true" class="h-5 w-5"><use href="/assets/sprite-CXh9QG8C.svg#pencil"></use></svg><span>Last modified on<!-- --> <time class="" dateTime="2022-05-22">May 22, 2022</time></span></span><p>Parts of this series:</p><ol><li>Spying on Functions and Changing Implementation</li><li><a href="/blog/mocking-with-jest/modules" data-discover="true">Taking Advantage of the Module System</a></li></ol><hr/><!--$?--><template id="B:0"></template><!--/$--><div class="text-right"><a class="p-2" href="#top">Back to top â</a></div></article></main><footer class="px-2.5"><hr class="mb-2!"/><div></div></footer></div>
21<script>((storageKey2, restoreKey) => { 22 if (!window.history.state || !window.history.state.key) { 23 let key = Math.random().toString(32).slice(2); 24 window.history.replaceState({ key }, ""); 25 } 26 try { 27 let positions = JSON.parse(sessionStorage.getItem(storageKey2) || "{}"); 28 let storedY = positions[restoreKey || window.history.state.key]; 29 if (typeof storedY === "number") { 30 window.scrollTo(0, storedY); 31 } 32 } catch (error) { 33 console.error(error); 34 sessionStorage.removeItem(storageKey2); 35 } 36 })("react-router-scroll-positions", null)</script>
36<script src="https://cdn.usefathom.com/script.js" data-site="GSHQIEZX" data-spa="history" data-auto="false" defer=""></script>
36<script>window.__reactRouterContext = {"basename":"/","future":{"v8_middleware":false,"unstable_optimizeDeps":false,"unstable_splitRouteModules":false,"unstable_subResourceIntegrity":false,"unstable_viteEnvironmentApi":false},"routeDiscovery":{"mode":"lazy","manifestPath":"/__manifest"},"ssr":true,"isSpaMode":false};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script>
36<script type="module" async="">; 37import * as route0 from "/assets/root-Ub0h2PaT.js"; 38import * as route1 from "/assets/post-series-DCWdy_vg.js"; 39 window.__reactRouterManifest = { 40 "entry": { 41 "module": "/assets/entry.client-BxZlRFGA.js", 42 "imports": [ 43 "/assets/chunk-UIGDSWPH-C1TJybnd.js", 44 "/assets/jsx-runtime-DlSKYi2Z.js", 45 "/assets/chunk-C4jv1B1e.js" 46 ], 47 "css": [] 48 }, 49 "routes": { 50 "root": { 51 "id": "root", 52 "path": "", 53 "hasAction": false, 54 "hasLoader": true, 55 "hasClientAction": false, 56 "hasClientLoader": false, 57 "hasClientMiddleware": false, 58 "hasErrorBoundary": true, 59 "module": "/assets/root-Ub0h2PaT.js", 60 "imports": [ 61 "/assets/chunk-UIGDSWPH-C1TJybnd.js", 62 "/assets/jsx-runtime-DlSKYi2Z.js", 63 "/assets/chunk-C4jv1B1e.js", 64 "/assets/cloudinary-DLG82wBp.js", 65 "/assets/dark-mode-Cu1p3Sgb.js" 66 ], 67 "css": [] 68 }, 69 "routes/post-series": { 70 "id": "routes/post-series", 71 "parentId": "root", 72 "path": "blog/:seriesSlug/:postSlug", 73 "hasAction": false, 74 "hasLoader": true, 75 "hasClientAction": false, 76 "hasClientLoader": false, 77 "hasClientMiddleware": false, 78 "hasErrorBoundary": false, 79 "module": "/assets/post-series-DCWdy_vg.js", 80 "imports": [ 81 "/assets/chunk-UIGDSWPH-C1TJybnd.js", 82 "/assets/Post-Bje60ncL.js", 83 "/assets/seo-CgzHf-Sn.js", 84 "/assets/cloudinary-DLG82wBp.js", 85 "/assets/dark-mode-Cu1p3Sgb.js", 86 "/assets/jsx-runtime-DlSKYi2Z.js", 87 "/assets/chunk-C4jv1B1e.js" 88 ], 89 "css": [] 90 }, 91 "routes/home": { 92 "id": "routes/home", 93 "parentId": "root", 94 "index": true, 95 "hasAction": false, 96 "hasLoader": true, 97 "hasClientAction": false, 98 "hasClientLoader": false, 99 "hasClientMiddleware": false, 100 "hasErrorBoundary": true, 101 "module": "/assets/home-DVd7QG1q.js", 102 "imports": [ 103 "/assets/chunk-UIGDSWPH-C1TJybnd.js", 104 "/assets/seo-CgzHf-Sn.js", 105 "/assets/cloudinary-DLG82wBp.js", 106 "/assets/jsx-runtime-DlSKYi2Z.js", 107 "/assets/chunk-C4jv1B1e.js" 108 ], 109 "css": [] 110 }, 111 "routes/post-standalone": { 112 "id": "routes/post-standalone", 113 "parentId": "root", 114 "path": "blog/:postSlug", 115 "hasAction": false, 116 "hasLoader": true, 117 "hasClientAction": false, 118 "hasClientLoader": false, 119 "hasClientMiddleware": false, 120 "hasErrorBoundary": false, 121 "module": "/assets/post-standalone-gud4cS98.js", 122 "imports": [ 123 "/assets/chunk-UIGDSWPH-C1TJybnd.js", 124 "/assets/Post-Bje60ncL.js", 125 "/assets/seo-CgzHf-Sn.js", 126 "/assets/cloudinary-DLG82wBp.js", 127 "/assets/dark-mode-Cu1p3Sgb.js", 128 "/assets/jsx-runtime-DlSKYi2Z.js", 129 "/assets/chunk-C4jv1B1e.js" 130 ], 131 "css": [] 132 }, 133 "routes/catchall": { 134 "id": "routes/catchall", 135 "parentId": "root", 136 "path": "*", 137 "hasAction": false, 138 "hasLoader": true, 139 "hasClientAction": false, 140 "hasClientLoader": false, 141 "hasClientMiddleware": false, 142 "hasErrorBoundary": false, 143 "module": "/assets/catchall-CjE5qGOy.js", 144 "imports": [ 145 "/assets/chunk-UIGDSWPH-C1TJybnd.js", 146 "/assets/chunk-C4jv1B1e.js" 147 ], 148 "css": [] 149 } 150 }, 151 "url": "/assets/manifest-101eee54.js", 152 "version": "101eee54" 153}; 154 window.__reactRouterRouteModules = {"root":route0,"routes/post-series":route1}; 155 156import("/assets/entry.client-BxZlRFGA.js");</script>
156<!--$-->
156<script>window.__reactRouterContext.streamController.enqueue("[{\"_1\":2,\"_39\":-5,\"_40\":-5},\"loaderData\",{\"_3\":4,\"_10\":11},\"root\",{\"_5\":6,\"_7\":8,\"_9\":-7},\"appName\",\"silvenon\",\"origin\",\"https://silvenon.com\",\"darkMode\",\"routes/post-series\",{\"_12\":13,\"_14\":15,\"_16\":-7,\"_17\":18,\"_19\":20,\"_21\":22,\"_32\":33},\"slug\",\"functions\",\"title\",\"Spying on Functions and Changing Implementation\",\"htmlTitle\",\"description\",\"Improve your unit testing experience by using Jest to record calls of particular methods and even changing their implementation as needed.\",\"seriesPart\",0,\"series\",{\"_12\":23,\"_14\":24,\"_25\":26,\"_27\":28},\"mocking-with-jest\",\"Mocking with Jest\",\"published\",\"2019-05-21\",\"parts\",[29,34],{\"_12\":13,\"_19\":20,\"_14\":15,\"_17\":18,\"_30\":31,\"_32\":33},\"category\",\"DEV\",\"lastModified\",\"2022-05-22\",{\"_12\":35,\"_19\":36,\"_14\":37,\"_17\":38,\"_30\":31,\"_32\":33},\"modules\",1,\"Taking Advantage of the Module System\",\"During testing you might not want certain modules to behave as usual, in that case you can use Jest to reimplement them!\",\"actionData\",\"errors\"]\n");</script>
156<!--$-->
156<script>window.__reactRouterContext.streamController.close();</script>
156<!--/$--><!--/$-->
156<script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>
156<div hidden id="S:0"><p>Jest has <strong>lots</strong> of mocking features. For a long time Iâve been using only a small subset of them, but with experience I was able to gain a deeper understanding of these features. Now I want to share that knowledge with you because it has been incredibly useful to me.</p> 157<p>This can be an intimidating area for beginners, especially because at the time of this writing the Jest documentation on this subject is a bit spotty. Add to that the fact that the term âmockâ is ambiguous; it can refer to functions, modules, servers etc. I would like to help you get familiar not only with mocking features in Jest, but these testing concepts in general.</p> 158<hr/> 159<p>Mock functions, are powerful and have many purposesâwe can create new dummy functions, spy on existing functions, temporarily change their implementation, pass them around⦠usually in order to eventually make assertions on them, directly or indirectly.</p> 160<h2>Spying</h2> 161<p>Letâs start with the most basic example. I assume you already know <a href="https://jestjs.io/docs/getting-started">how to set up Jest</a>? Good. Letâs open a test file:</p> 162<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6">const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> fn</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> jest</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span></code></pre> 163<p>This is a dummy function. We can call it, but nothing seems to happen.</p> 164<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955"> // nothing...?</span></span> 165<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">({</span><span style="--shiki-light:#998418;--shiki-dark:#9CDCFE"> meaningOfLife</span><span style="--shiki-light:#999999;--shiki-dark:#9CDCFE">:</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8"> 42</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955"> // still nothing</span></span> 166<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">console</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">log</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">())</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955"> // undefined</span></span></code></pre> 167<p>However, if we inspect the function itself, weâll notice that itâs no ordinary function:</p> 168<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">console</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">log</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 169<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// { [Function: mockConstructor]</span></span> 170<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// _isMockFunction: true,</span></span> 171<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// getMockImplementation: [Function],</span></span> 172<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// mock: [Getter/Setter],</span></span> 173<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// ...</span></span></code></pre> 174<p>For example, letâs inspect its <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>mock</span></span></code></span> property:</p> 175<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">console</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">log</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">mock</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">calls</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 176<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// [ [], [ { meaningOfLife: 42 } ], [] ]</span></span></code></pre> 177<p>Looks familiar? It appears that the mock function has been recording our calls! Donât panic, not phone calls, just function calls. This means that we can make assertions on this function, but instead of making assertions on the <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>mock</span></span></code></span> property directly, we can use special Jest matchers for mock functions:</p> 178<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">test</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">mock function has been called with the meaning of life</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 179<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> fn</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> jest</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 180<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8">42</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 181<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> expect</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">).</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">toHaveBeenCalledWith</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8">42</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 182<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">})</span></span></code></pre> 183<p>But this test is silly, we already know that the function will be called with <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>42</span></span></code></span> because <em>we</em>
183 called it within the test itself. We usually want to pass mock functions to the implementation code that weâre testing, then we expect that code to call them in a certain way. For example, letâs say that we want to test the following code:</p> 184<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// monty-python.js</span></span> 185<span data-line=""><span style="--shiki-light:#998418;--shiki-dark:#4EC9B0">module</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#998418;--shiki-dark:#4EC9B0">exports</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#4EC9B0"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 186<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> callFnWithTheMeaningOfLife</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 187<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8">42</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 188<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> }</span></span> 189<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">}</span></span></code></pre> 190<p>To test whether <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>callFnWithTheMeaningOfLife</span></span></code></span> indeed calls the given function <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>fn</span></span></code></span> with the meaning of life, we can pass a mock function:</p> 191<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6">const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> require</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">./monty-python</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 192<span data-line=""> </span> 193<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// ok, I wasn't planning on continuing with this</span></span> 194<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955">// Monty Python reference, but I guess we're doing this ð</span></span> 195<span data-line=""> </span> 196<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">describe</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">MontyPython</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 197<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> describe</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">callFnWithTheMeaningOfLife</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 198<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> it</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">calls given function with the meaning of life</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 199<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> montyPython</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> new</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 200<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> fn</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> jest</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 201<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> montyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">callFnWithTheMeaningOfLife</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 202<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> expect</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">fn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">).</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">toHaveBeenCalledWith</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8">42</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 203<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span></span> 204<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span></span> 205<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">})</span></span></code></pre> 206<p>But what if the meaning of life isnât 42? Life is never that simple, things often donât happen for a reason, theyâre just random, and itâs up to us to try to make the best of it. â¤ï¸</p> 207<p>Now that we had this life-changing epiphany, letâs make our implementation a bit more realistic, i.e. <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random()</span></span></code></span>:</p> 208<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#998418;--shiki-dark:#4EC9B0">module</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#998418;--shiki-dark:#4EC9B0">exports</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#4EC9B0"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 209<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> getTheMeaningOfLife</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 210<span data-line=""><span style="--shiki-light:#1E754F;--shiki-dark:#C586C0"> return</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> Math</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">random</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#D4D4D4"> *</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8"> 100</span></span> 211<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> }</span></span> 212<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">}</span></span></code></pre> 213<p>Thatâs more like it. In real life you rarely get a clean 42, usually you get something like 19.513179535940452 and you have to roll with it.</p> 214<p>Now letâs adjust our test. Now we want to check whether <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span> has been called. We canât just replace <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span> with a mock function because we want to preserve its functionality, instead we can spy on it using <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>jest.spyOn</span></span></code></span>, which wraps it in a mock function and returns it so we can track it:</p> 215<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6">const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> require</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">./monty-python</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 216<span data-line=""> </span> 217<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">describe</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">MontyPython</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 218<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> describe</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">getTheMeaningOfLife</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 219<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> it</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">reveals the cold hard truth about life</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 220<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> montyPython</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> new</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 221<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> mathRandomSpy</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> jest</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">spyOn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">Math</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">random</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 222<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> montyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">getTheMeaningOfLife</span><span style="--shiki-li
222ght:#999999;--shiki-dark:#E6E6E6">()</span></span> 223<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> expect</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">mathRandomSpy</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">).</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">toHaveBeenCalled</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 224<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> mathRandomSpy</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">mockRestore</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 225<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span></span> 226<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span></span> 227<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">})</span></span></code></pre> 228<p>Here <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span> does its thing, weâre not changing its functionality, weâre only temporarily wrapping it in a mock function in order to make assertions on it. Then at the end of the test weâre removing the wrapper because we no longer need it.</p> 229<h2>Changing implementation</h2> 230<p>Keep in mind that testing is about <em>purpose</em>, weâll usually want to test overall functionality, not details like whether built-ins like <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span> were called. To do this weâll probably want to change <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span>âs behavior in order to be able to test a larger piece of functionality. This is where the <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>mockImplementation</span></span></code></span> method comes in.</p> 231<p>Letâs say that the head of the <a href="https://youtu.be/eCLp7zodUiI">Ministry of Silly Walks</a> wanted to add a method for plotting their walking pattern as an array of steps using left and right legs:</p> 232<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#998418;--shiki-dark:#4EC9B0">module</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#998418;--shiki-dark:#4EC9B0">exports</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#4EC9B0"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 233<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> getSillyWalk</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">numberOfSteps</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 234<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> steps</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> []</span></span> 235<span data-line=""><span style="--shiki-light:#1E754F;--shiki-dark:#C586C0"> for</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> i</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">;</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> i</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> <</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> numberOfSteps</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">;</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> i</span><span style="--shiki-light:#AB5959;--shiki-dark:#D4D4D4">++</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 236<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> previousStep</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> steps</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">[</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">i</span><span style="--shiki-light:#AB5959;--shiki-dark:#D4D4D4"> -</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8"> 1</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">]</span></span> 237<span data-line=""><span style="--shiki-light:#1E754F;--shiki-dark:#C586C0"> if</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> (</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">previousStep</span><span style="--shiki-light:#AB5959;--shiki-dark:#D4D4D4"> ===</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">left</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 238<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> steps</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">push</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">right</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 239<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> }</span><span style="--shiki-light:#1E754F;--shiki-dark:#C586C0"> else</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 240<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> steps</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">push</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">left</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 241<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> }</span></span> 242<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> }</span></span> 243<span data-line=""><span style="--shiki-light:#A0ADA0;--shiki-dark:#6A9955"> // shuffle</span></span> 244<span data-line=""><span style="--shiki-light:#1E754F;--shiki-dark:#C586C0"> return</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> steps</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">
244sort</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8"> 0.5</span><span style="--shiki-light:#AB5959;--shiki-dark:#D4D4D4"> -</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> Math</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">random</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">())</span></span> 245<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> }</span></span> 246<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">}</span></span></code></pre> 247<p>We would like to test several steps of this walk, but as they are random this isnât really possible, is it? We canât tell our test which steps weâre expecting, so we canât test whether the walk is silly, we can only test whether itâs indeed a walk by defining it as a series of regular steps, with one leg after another. To do this we have to alter the behavior of <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span> to always return <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>0.5</span></span></code></span> using the <a href="https://jestjs.io/docs/mock-function-api#mockfnmockimplementationfn"><span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>mockImplementation</span></span></code></span></a> method, preserving the order of steps (as long as <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>sort</span></span></code></span>âs function returns <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>0</span></span></code></span>, the order stays as-is). Finally, to avoid having to type out the expected array of steps, weâll use <a href="https://jestjs.io/docs/snapshot-testing">Jestâs snapshots</a> to test this:</p> 248<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="js" data-theme="vitesse-light slack-dark"><code data-language="js" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6">const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> require</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">./monty-python</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 249<span data-line=""> </span> 250<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">describe</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">MontyPython</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 251<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> describe</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">getSillyWalk</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 252<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> it</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">returns a series of steps for each leg</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> ()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> {</span></span> 253<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> montyPython</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> new</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> MontyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 254<span data-line=""><span style="--shiki-light:#AB5959;--shiki-dark:#569CD6"> const</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> mathRandomSpy</span><span style="--shiki-light:#999999;--shiki-dark:#D4D4D4"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> jest</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">spyOn</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">Math</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">random</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">'</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 255<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> mathRandomSpy</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">mockImplementation</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(()</span><span style="--shiki-light:#999999;--shiki-dark:#569CD6"> =></span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8"> 0.5</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)</span></span> 256<span data-line=""><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA"> expect</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE">montyPython</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">getSillyWalk</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#B5CEA8">6</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">)).</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">toMatchSnapshot</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 257<span data-line=""><span style="--shiki-light:#B07D48;--shiki-dark:#9CDCFE"> mathRandomSpy</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">.</span><span style="--shiki-light:#59873A;--shiki-dark:#DCDCAA">mockRestore</span><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">()</span></span> 258<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span></span> 259<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6"> })</span></span> 260<span data-line=""><span style="--shiki-light:#999999;--shiki-dark:#E6E6E6">})</span></span></code></pre> 261<p>Now when we run our tests, the following deterministic snapshot will be saved:</p> 262<pre style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222" tabindex="0" data-language="txt" data-theme="vitesse-light slack-dark"><code data-language="txt" data-theme="vitesse-light slack-dark" style="display:grid"><span data-line=""><span>exports[`MontyPython getSillyWalk returns a series of steps for each leg 1`] = `</span></span> 263<span data-line=""><span>Array [</span></span> 264<span data-line=""><span> "left",</span></span> 265<span data-line=""><span> "right",</span></span> 266<span data-line=""><span> "left",</span></span> 267<span data-line=""><span> "right",</span></span> 268<span data-line=""><span> "left",</span></span> 269<span data-line=""><span> "right",</span></span> 270<span data-line=""><span>]</span></span> 271<span data-line=""><span>`;</span></span></code></pre> 272<p>Notice that we didnât make assertions on the spy itself, we just temporarily altered <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>Math.random</span></span></code></span>âs behavior so we can make a predictable assertion on the code that it was <em>affecting</em>.</p> 273<p>Now, since our codebase is split across files, letâs start exploring mocking in the context of <em>modules</em>.</p> 274<h2>Conclusion</h2> 275<p>It took me a long time to understand the nuances of these features, how to get what I want and how to even <em>know</em> what I want. I encourage you to scroll through the <a href="https://jestjs.io/docs/expect"><span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>expect</span></span></code></span> reference</a> to learn more about these features and how they compare to the ones that I didnât cover in this post. If you catch yourself making assertions on the <span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>mock</span></span></code></span> property directly, try to see if thereâs already a built-in matcher for the assertion youâre looking for, maybe also combining them with utilities like <a href="https://jestjs.io/docs/expect#expectobjectcontainingobject"><span data-rehype-pretty-code-figure=""><code data-language="plaintext" data-theme="vitesse-light slack-dark" style="--shiki-light:#393a34;--shiki-dark:#E6E6E6;--shiki-light-bg:#ffffff;--shiki-dark-bg:#222222"><span data-line=""><span>expect.objectContaining</span></span></code></span></a>. This has the benefit of being more readable and having a better error message if your test fails.</p> 276<p>I hope that this post brought you some clarity on the subject, have fun building better tests!</p> 277<hr/> 278<p>What about modules? See how you can mock modules on different levels by <a href="/blog/mocking-with-jest/modules">taking advantage of the module 279system</a>.</p></div>
279<script>$RB=[];$RV=function(a){$RT=performance.now();for(var b=0;b<a.length;b+=2){var c=a[b],e=a[b+1];null!==e.parentNode&&e.parentNode.removeChild(e);var f=c.parentNode;if(f){var g=c.previousSibling,h=0;do{if(c&&8===c.nodeType){var d=c.data;if("/$"===d||"/&"===d)if(0===h)break;else h--;else"$"!==d&&"$?"!==d&&"$~"!==d&&"$!"!==d&&"&"!==d||h++}d=c.nextSibling;f.removeChild(c);c=d}while(c);for(;e.firstChild;)f.insertBefore(e.firstChild,c);g.data="$";g._reactRetry&&requestAnimationFrame(g._reactRetry)}}a.length=0}; 280$RC=function(a,b){if(b=document.getElementById(b))(a=document.getElementById(a))?(a.previousSibling.data="$~",$RB.push(a,b),2===$RB.length&&("number"!==typeof $RT?requestAnimationFrame($RV.bind(null,$RB)):(a=performance.now(),setTimeout($RV.bind(null,$RB),2300>a&&2E3<a?2300-a:$RT+300-a)))):b.parentNode.removeChild(b)};$RC("B:0","S:0")</script>
280</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.