PageSourceSearch

https://silvenon.com/blog/mocking-with-jest/functions

html silvenon.com collected 2026-10-02 06:59:44 UTC 63,530 bytes, 280 lines download raw bytes

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">&#x27;</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">&#x27;</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"> =&gt;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">./monty-python</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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&#x27;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&#x27;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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">MontyPython</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">callFnWithTheMeaningOfLife</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</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">&#x27;</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"> =&gt;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">./monty-python</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">MontyPython</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">getTheMeaningOfLife</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</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">&#x27;</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"> =&gt;</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"> &#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">random</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> &lt;</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"> &#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">left</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">right</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">left</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">./monty-python</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">MontyPython</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">getSillyWalk</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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">&#x27;</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">&#x27;</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"> =&gt;</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"> &#x27;</span><span style="--shiki-light:#B56959;--shiki-dark:#CE9178">random</span><span style="--shiki-light:#B5695977;--shiki-dark:#CE9178">&#x27;</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"> =&gt;</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>  &quot;left&quot;,</span></span>
265<span data-line=""><span>  &quot;right&quot;,</span></span>
266<span data-line=""><span>  &quot;left&quot;,</span></span>
267<span data-line=""><span>  &quot;right&quot;,</span></span>
268<span data-line=""><span>  &quot;left&quot;,</span></span>
269<span data-line=""><span>  &quot;right&quot;,</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.