316 lines
No EOL
25 KiB
HTML
316 lines
No EOL
25 KiB
HTML
<!DOCTYPE html><html><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type" /><meta content="width=device-width, initial-scale=1" name="viewport" /><!--replace-start-0--><!--replace-start-5--><!--replace-start-8--><title>Testing with Jest - My Zettelkasten</title><!--replace-end-8--><!--replace-end-5--><!--replace-end-0--><link href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.css" rel="stylesheet" /><link href="https://fonts.googleapis.com/css?family=Merriweather|Libre+Franklin|Roboto+Mono&display=swap" rel="stylesheet" /><!--replace-start-1--><!--replace-start-4--><!--replace-start-7--><link href="https://raw.githubusercontent.com/srid/neuron/master/assets/neuron.svg" rel="icon" /><meta content="Let’ say we have this class:" name="description" /><meta content="Testing with Jest" property="og:title" /><meta content="My Zettelkasten" property="og:site_name" /><meta content="article" property="og:type" /><meta content="Testing_with_Jest" property="neuron:zettel-id" /><meta content="Testing_with_Jest" property="neuron:zettel-slug" /><meta content="javascript" property="neuron:zettel-tag" /><meta content="jest" property="neuron:zettel-tag" /><meta content="testing" property="neuron:zettel-tag" /><script type="application/ld+json">[]</script><style type="text/css">body{background-color:#eeeeee !important;font-family:"Libre Franklin", serif !important}body .ui.container{font-family:"Libre Franklin", serif !important}body h1, h2, h3, h4, h5, h6, .ui.header, .headerFont{font-family:"Merriweather", sans-serif !important}body code, pre, tt, .monoFont{font-family:"Roboto Mono","SFMono-Regular","Menlo","Monaco","Consolas","Liberation Mono","Courier New", monospace !important}body div.z-index p.info{color:#808080}body div.z-index ul{list-style-type:square;padding-left:1.5em}body div.z-index .uplinks{margin-left:0.29999em}body .zettel-content h1#title-h1{background-color:rgba(33,133,208,0.1)}body nav.bottomPane{background-color:rgba(33,133,208,2.0e-2)}body div#footnotes{border-top-color:#2185d0}body p{line-height:150%}body img{max-width:100%}body .deemphasized{font-size:0.94999em}body .deemphasized:hover{opacity:1}body .deemphasized:not(:hover){opacity:0.69999}body .deemphasized:not(:hover) a{color:#808080 !important}body div.container.universe{padding-top:1em}body div.zettel-view ul{padding-left:1.5em;list-style-type:square}body div.zettel-view .pandoc .highlight{background-color:#ffff00}body div.zettel-view .pandoc .ui.disabled.fitted.checkbox{margin-right:0.29999em;vertical-align:middle}body div.zettel-view .zettel-content .metadata{margin-top:1em}body div.zettel-view .zettel-content .metadata div.date{text-align:center;color:#808080}body div.zettel-view .zettel-content h1{padding-top:0.2em;padding-bottom:0.2em;text-align:center}body div.zettel-view .zettel-content h2{border-bottom:solid 1px #4682b4;margin-bottom:0.5em}body div.zettel-view .zettel-content h3{margin:0px 0px 0.4em 0px}body div.zettel-view .zettel-content h4{opacity:0.8}body div.zettel-view .zettel-content div#footnotes{margin-top:4em;border-top-style:groove;border-top-width:2px;font-size:0.9em}body div.zettel-view .zettel-content div#footnotes ol > li > p:only-of-type{display:inline;margin-right:0.5em}body div.zettel-view .zettel-content aside.footnote-inline{width:30%;padding-left:15px;margin-left:15px;float:right;background-color:#d3d3d3}body div.zettel-view .zettel-content .overflows{overflow:auto}body div.zettel-view .zettel-content code{margin:auto auto auto auto;font-size:100%}body div.zettel-view .zettel-content p code, li code, ol code{padding:0.2em 0.2em 0.2em 0.2em;background-color:#f5f2f0}body div.zettel-view .zettel-content pre{overflow:auto}body div.zettel-view .zettel-content dl dt{font-weight:bold}body div.zettel-view .zettel-content blockquote{background-color:#f9f9f9;border-left:solid 10px #cccccc;margin:1.5em 0px 1.5em 0px;padding:0.5em 10px 0.5em 10px}body div.zettel-view .zettel-content.raw{background-color:#dddddd}body .ui.label.zettel-tag{color:#000000}body .ui.label.zettel-tag a{color:#000000}body nav.bottomPane ul.backlinks > li{padding-bottom:0.4em;list-style-type:disc}body nav.bottomPane ul.context-list > li{list-style-type:lower-roman}body .footer-version img{-webkit-filter:grayscale(100%);-moz-filter:grayscale(100%);-ms-filter:grayscale(100%);-o-filter:grayscale(100%);filter:grayscale(100%)}body .footer-version img:hover{-webkit-filter:grayscale(0%);-moz-filter:grayscale(0%);-ms-filter:grayscale(0%);-o-filter:grayscale(0%);filter:grayscale(0%)}body .footer-version, .footer-version a, .footer-version a:visited{color:#808080}body .footer-version a{font-weight:bold}body .footer-version{margin-top:1em !important;font-size:0.69999em}@media only screen and (max-width: 768px){body div#zettel-container{margin-left:0.4em !important;margin-right:0.4em !important}}body span.zettel-link-container span.zettel-link a{color:#2185d0;font-weight:bold;text-decoration:none}body span.zettel-link-container span.zettel-link a:hover{background-color:rgba(33,133,208,0.1)}body span.zettel-link-container span.extra{color:auto}body span.zettel-link-container.errors{border:solid 1px #ff0000}body span.zettel-link-container.errors span.zettel-link a:hover{text-decoration:none !important;cursor:not-allowed}body [data-tooltip]:after{font-size:0.69999em}body div.tag-tree div.node{font-weight:bold}body div.tag-tree div.node a.inactive{color:#555555}body .tree.flipped{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}body .tree{overflow:auto}body .tree ul.root{padding-top:0px;margin-top:0px}body .tree ul{position:relative;padding:1em 0px 0px 0px;white-space:nowrap;margin:0px auto 0px auto;text-align:center}body .tree ul::after{content:"";display:table;clear:both}body .tree ul:last-child{padding-bottom:0.1em}body .tree li{display:inline-block;vertical-align:top;text-align:center;list-style-type:none;position:relative;padding:1em 0.5em 0em 0.5em}body .tree li::before{content:"";position:absolute;top:0px;right:50%;border-top:solid 2px #cccccc;width:50%;height:1.19999em}body .tree li::after{content:"";position:absolute;top:0px;right:50%;border-top:solid 2px #cccccc;width:50%;height:1.19999em}body .tree li::after{right:auto;left:50%;border-left:solid 2px #cccccc}body .tree li:only-child{padding-top:0em}body .tree li:only-child::after{display:none}body .tree li:only-child::before{display:none}body .tree li:first-child::before{border-style:none;border-width:0px}body .tree li:first-child::after{border-radius:5px 0px 0px 0px}body .tree li:last-child::after{border-style:none;border-width:0px}body .tree li:last-child::before{border-right:solid 2px #cccccc;border-radius:0px 5px 0px 0px}body .tree ul ul::before{content:"";position:absolute;top:0px;left:50%;border-left:solid 2px #cccccc;width:0px;height:1.19999em}body .tree li div.forest-link{border:solid 2px #cccccc;padding:0.2em 0.29999em 0.2em 0.29999em;text-decoration:none;display:inline-block;border-radius:5px 5px 5px 5px;color:#333333;position:relative;top:2px}body .tree.flipped li div.forest-link{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}</style><script
|
||
async=""
|
||
id="MathJax-script"
|
||
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
|
||
></script>
|
||
<link
|
||
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
|
||
rel="stylesheet"
|
||
/><link rel="preconnect" href="https://fonts.googleapis.com" /><link
|
||
rel="preconnect"
|
||
href="https://fonts.gstatic.com"
|
||
crossorigin
|
||
/><link
|
||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Serif:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/components/prism-core.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||
<style>
|
||
body .ui.container,
|
||
body ul {
|
||
font-family: "IBM Plex Sans" !important;
|
||
}
|
||
body blockquote {
|
||
border-left-width: 3px !important;
|
||
font-style: italic;
|
||
}
|
||
.headerFont,
|
||
.ui.header,
|
||
body h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
font-family: "IBM Plex Sans Condensed" !important;
|
||
}
|
||
body p {
|
||
line-height: 1.4;
|
||
}
|
||
.monoFont,
|
||
body code,
|
||
pre,
|
||
tt {
|
||
font-family: "IBM Plex Mono" !important;
|
||
font-size: 12px !important;
|
||
line-height: 1.4 !important;
|
||
}
|
||
</style>
|
||
<!--replace-end-7--><!--replace-end-4--><!--replace-end-1--></head><body><div class="ui fluid container universe"><!--replace-start-2--><!--replace-start-3--><!--replace-start-6--><div class="ui text container" id="zettel-container" style="position: relative"><div class="zettel-view"><article class="ui raised attached segment zettel-content"><div class="pandoc"><h1 id="title-h1">Testing with Jest</h1><h2 id="mocking-classesmodules">Mocking classes/modules</h2><h3 id="classes">Classes</h3><p>Let’ say we have this class:</p><pre><code class="js language-js">// database.js
|
||
class Database {
|
||
connect() {}
|
||
save(data) {}
|
||
}</code></pre><p>Then to mock:</p><pre><code class="js language-js">import Database from "./database";
|
||
|
||
// This will mock the whole Database class, replacing all methods with jest mock functions.
|
||
jest.mock("./database");
|
||
|
||
test("should use mocked save method", () => {
|
||
const dbInstance = new Database();
|
||
|
||
// Mocking the save method with a specific return value
|
||
dbInstance.save.mockReturnValue(true);
|
||
|
||
const result = dbInstance.save({ key: "value" });
|
||
|
||
expect(result).toBe(true);
|
||
expect(dbInstance.save).toHaveBeenCalledWith({ key: "value" });
|
||
|
||
// The connect method is still a mock function (but without a specific behavior).
|
||
dbInstance.connect();
|
||
expect(dbInstance.connect).toHaveBeenCalled();
|
||
});</code></pre><h3 id="modules">Modules</h3><p>Say we have the following module file:</p><pre><code class="js language-js">// utils.js
|
||
export const doSomething = () => {
|
||
// ...
|
||
};
|
||
|
||
export const fetchUserData = async (userId) => {
|
||
const response = await axios.get(`/api/users/${userId}`);
|
||
return response.data;
|
||
};</code></pre><p>Mocked:</p><pre><code class="js language-js">jest.mock("./utils", () => {
|
||
return {
|
||
doSomething: jest.fn(() => "mocked doSomething"),
|
||
fetchUserData: jest.fn((userId) =>
|
||
Promise.resolve({ id: userId, name: "Mock User" })
|
||
),
|
||
};
|
||
});
|
||
|
||
test("should use mocked module functions", () => {
|
||
expect(utils.doSomething()).toBe("mocked doSomething");
|
||
expect(utils.doSomething).toHaveBeenCalled();
|
||
|
||
const result = await utils.fetchUserData(123);
|
||
|
||
expect(result).toEqual({ id: 123, name: "Mock User" });
|
||
expect(utils.fetchUserData).toHaveBeenCalledWith(123);
|
||
|
||
});</code></pre><h3 id="inline-mocking-versus-per-test-mocking">Inline mocking versus “per test” mocking</h3><p>There are two different architectures that we can use when mocking modules and classes: <strong>inline</strong> and <strong>per test</strong> mocking.</p><p>Here is the inline case:</p><pre><code class="js language-js">jest.mock("./some_module.js", () => {
|
||
return {
|
||
someFunction: jest.fn(() => "value"),
|
||
someFunctionWithParam: jest.fn((param) => ({
|
||
property: param,
|
||
})),
|
||
someAsyncFunction: jest.fn(() => Promise.resolve("value")),
|
||
};
|
||
});</code></pre><p>Here is the per test case:</p><pre><code class="js language-js">import { someModule } from "./some_module.js";
|
||
|
||
let someModuleMock;
|
||
|
||
someModuleMock = {
|
||
someFunction: jest.fn(() => "value"),
|
||
someFunctionWithParam: jest.fn((param) => ({
|
||
property: param,
|
||
})),
|
||
someAsyncFunction: jest.fn(() => Promise.resolve("value")),
|
||
};
|
||
|
||
someModule.mockImplementation(() => someModuleMock);
|
||
|
||
it("should do something", () => {
|
||
const newValue = "new value";
|
||
someModule.someFunction.mockReturnValue(newValue);
|
||
});</code></pre><p>The benefits of inline:</p><ul><li>Inline is good because everything is set up in one place</li><li>Inline keeps consistency accross tests: every test case in the file will use the same mocked function unless overwritten within a test</li><li>It lends itself to being a <em>global</em> mock that can be used accross test files in a <code>__mocks__/</code> directory</li></ul><p>The benefits of per-test:</p><ul><li>You can very mock implementations within the file, providing more granular control. You can redefine <code>someModuleMock</code> or parts of it (<code>someModule.someFunction</code>) throughout your test file to accomodate varied requirements between tests</li><li>It’s beneficial when your tests have divergent requirements, as you can perform more detailed setups and overrides for each individual test case or suite, ensuring mocks are configured exactly as required.</li></ul><h4 id="overriding-inline-mocks">Overriding inline mocks</h4><p>Per test mocking makes it straightforward to change the test parameters of the mocked module or class but you can also override inline mocks.</p><p>If we were using the <code>someModule</code> inline mock and we wanted to override the <code>someFunction</code> function that we have defined inline, we would first import the <code>someFunction</code> function and then use <code>mockImplementation</code> against it:</p><pre><code class="js language-js">import { someFunction } from "./some_module.js";
|
||
someFunction.mockImplementation(() => "custom value");
|
||
expect(someFunction()).toBe("custom value");
|
||
|
||
// Optional: Restore the original mock implementation after the test
|
||
someFunction.mockRestore();</code></pre><p>Note: although we are importing <code>someFunction</code> we are not actually importing the real function tha belongs to the module. Because Jest mocks all of its properties and methods with the inline syntax, we are actually just importing that which Jest has aready mocked, but the syntax is a bit misleading.</p><h4 id="applied-to-classes">Applied to classes</h4><p>The same approaches (with minor differences) can be used with classes:</p><p>Using inline (where the class is not the default export):</p><pre><code class="js language-js">jest.mock("./SomeClass", () => {
|
||
return {
|
||
SomeClass: jest.fn().mockImplementation(() => {
|
||
return {
|
||
someFunction: jest.fn(() => "value"),
|
||
someFunctionWithParam: jest.fn((param) => ({ property: param })),
|
||
someAsyncFunction: jest.fn(() => Promise.resolve("value")),
|
||
someOtherFunctionThatResolves: jest.fn().mockResolvedValue("some data"),
|
||
};
|
||
}),
|
||
};
|
||
});</code></pre><p>Using per test:</p><pre><code class="js language-js">import SomeClass from "./someClass";
|
||
|
||
jest.mock("./someClass");
|
||
|
||
let someClassMock = {
|
||
someFunction: jest.fn(() => "value"),
|
||
someFunctionWithParam: jest.fn((param) => ({ property: param })),
|
||
someAsyncFunction: jest.fn(() => Promise.resolve("value")),
|
||
};
|
||
|
||
// Mock class implementation
|
||
SomeClass.mockImplementation(() => someClassMock);
|
||
|
||
it("should do something", () => {
|
||
const newValue = "new value";
|
||
someClassMock.someFunction.mockReturnValue(newValue);
|
||
});</code></pre><h2 id="check-that-a-function-has-been-called-within-another-function">Check that a function has been called within another function</h2><pre><code class="js language-js">function toBeCalledFunction() {
|
||
console.log("Original function called");
|
||
}
|
||
|
||
function callerFunction() {
|
||
toBeCalledFunction();
|
||
}</code></pre><pre><code class="js language-js">test("spy on toBeCalledFunction", () => {
|
||
const spy = jest.spyOn(global, "toBeCalledFunction"); // Replace `global` with the appropriate object/context if the function is not global
|
||
callerFunction();
|
||
expect(spy).toHaveBeenCalled();
|
||
spy.mockRestore(); // Restore the original function after spying
|
||
});</code></pre><h2 id="mock-a-function-that-needs-to-resolve-to-something-within-another-function">Mock a function that needs to resolve to something within another function</h2><p>We have two functions, one that gets data and another that processes it. We want to mock the function that gets data and return a value that the processing function can use.</p><pre><code class="js language-js">async function getData() {
|
||
// ... Fetch some data from an API or database
|
||
return fetchedData;
|
||
}
|
||
|
||
async function processData() {
|
||
const data = await getData();
|
||
// ... Process the data
|
||
return processedData;
|
||
}</code></pre><p>The mocking part:</p><pre><code class="js language-js">const mockData = { key: "value" }; // Mocked data
|
||
|
||
jest.mock("./path-to-file-where-getData-is", () => ({
|
||
getData: jest.fn().mockResolvedValue(mockData),
|
||
}));
|
||
|
||
test("test processData function", async () => {
|
||
const result = await processData();
|
||
// Now, result contains the processed version of mockData
|
||
expect(result).toEqual(/* expected processed data based on mockData */);
|
||
});</code></pre><p>We could also combine the above with a spy to check that the <code>getData</code> function was called:</p><pre><code class="js language-js">const getDataSpy = jest
|
||
.spyOn(moduleContainingGetData, "getData")
|
||
.mockResolvedValue(mockData);
|
||
|
||
const result = await processData();
|
||
expect(getDataSpy).toHaveBeenCalled();
|
||
expect(result).toEqual(/* expected processed data based on mockData */);
|
||
getDataSpy.mockRestore();</code></pre><h2 id="mock-a-function-that-takes-arguments">Mock a function that takes arguments</h2><pre><code class="js language-js">function addPrefix(str) {
|
||
return `prefix-${str}`;
|
||
}</code></pre><pre><code class="js language-js">test("dynamic mock for addPrefix function", () => {
|
||
const mockFunction = jest.fn((str) => `mock-${str}`);
|
||
|
||
// Example usage of mockFunction
|
||
const result1 = mockFunction("test");
|
||
const result2 = mockFunction("example");
|
||
|
||
expect(result1).toBe("mock-test");
|
||
expect(result2).toBe("mock-example");
|
||
});</code></pre><h2 id="mocking-network-requests">Mocking network requests</h2><h3 id="mocking-axios">Mocking Axios</h3><pre><code class="js language-js">jest.mock("axios", () => ({
|
||
get: jest.fn().mockResolvedValue(mockData),
|
||
post: jest.fn().mockResolvedValue(mockData),
|
||
}));</code></pre><p>Or we could implement this way:</p><pre><code class="js language-js">jest.mock("axios");
|
||
axios.get.mockResolvedValue({ data: "mockedData" });
|
||
axios.post.mockResolvedValue({ data: "mockedData" });</code></pre><p>Then we can use the mocked axios functions in our tests:</p><pre><code class="js language-js">const result = await fetchData(); // the function that uses Axios `get``
|
||
expect(result).toBe("mockedGetData");
|
||
|
||
const result = await sendData({ key: "value" }); // the function tha uses Axios `post`
|
||
expect(result).toBe("mockedPostData");</code></pre><h3 id="mockimplementation"><code>mockImplementation</code></h3><p>For more configurable cases we can use <code>mockImplementation</code>:</p><pre><code class="js language-js">it("sends data", async () => {
|
||
// Mock axios.post using mockImplementation
|
||
axios.post.mockImplementation((url, data) => {
|
||
if (data.key === "value") {
|
||
return Promise.resolve({ data: "mockedPostData" });
|
||
} else {
|
||
return Promise.reject({ error: "An error occurred" });
|
||
}
|
||
});
|
||
|
||
const result = await sendData({ key: "value" });
|
||
|
||
expect(result).toBe("mockedPostData");
|
||
});</code></pre><p>If we want to change the <code>get</code> and <code>post</code> values in different tests, we can do so by using <code>mockImplementation</code>:</p><h2 id="mocking-exceptions">Mocking exceptions</h2><p>Again we use <code>mockImplementation</code>:</p><p>Say we have the following function:</p><pre><code class="js language-js">// fetchData.js
|
||
import axios from "axios";
|
||
|
||
const fetchData = async (url) => {
|
||
try {
|
||
const response = await axios.get(url);
|
||
return response.data;
|
||
} catch (error) {
|
||
throw new Error("Error fetching data");
|
||
}
|
||
};
|
||
|
||
export default fetchData;</code></pre><p>We would mock the success and the error as follows:</p><pre><code class="js language-js">import axios from "axios";
|
||
import fetchData from "./fetchData";
|
||
|
||
jest.mock("axios");
|
||
|
||
describe("fetchData", () => {
|
||
it("fetches data successfully", async () => {
|
||
axios.get.mockResolvedValue({ data: "mockedData" });
|
||
|
||
const result = await fetchData("https://api.example.com/data");
|
||
|
||
expect(result).toBe("mockedData");
|
||
});
|
||
|
||
it("throws an error when fetching fails", async () => {
|
||
axios.get.mockImplementation(() => {
|
||
throw new Error("API error");
|
||
});
|
||
|
||
// We use an asynchronous assertion here because we're expecting a promise to reject
|
||
await expect(fetchData("https://api.example.com/data")).rejects.toThrow(
|
||
"Error fetching data"
|
||
);
|
||
});
|
||
});</code></pre><h2 id="parameterization">Parameterization</h2><p>The following offers a good opportunity for parameterisation:</p><pre><code class="js language-js">it("should return page for deletion from `ipages-live`", async () => {
|
||
// preview = false, isInternal = false
|
||
await deletePageFromS3("url", false, false);
|
||
const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
|
||
expect(deleteObjectCommand.input).toEqual({
|
||
Bucket: "bbc-ise-ipages-live",
|
||
Key: "url/index.html",
|
||
});
|
||
});
|
||
|
||
it("should return page for deletion from `preview`", async () => {
|
||
// preview = true, isInternal = false
|
||
await deletePageFromS3("url", true, false);
|
||
const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
|
||
expect(deleteObjectCommand.input).toEqual({
|
||
Bucket: "staff.bbc.com-preview",
|
||
Key: "preview/url/index.html",
|
||
});
|
||
});
|
||
|
||
...</code></pre><p>Each time we are passing in three parameters to the <code>deletePageFromS3</code> function which is the object under test. Each time there are different variations in the object that is output.</p><p>To parameterize the process rather than use repeated <code>it</code> blocks we can combine the input paramters and outputs into an array:</p><pre><code class="js language-js">const testParams = [
|
||
{
|
||
preview: false,
|
||
isInternal: false,
|
||
bucket: "ipages-live",
|
||
key: "url/index.html",
|
||
},
|
||
{
|
||
preview: true,
|
||
isInternal: false,
|
||
bucket: "staff.com-preview",
|
||
key: "preview/url/index.html",
|
||
},
|
||
];</code></pre><p>Then use <code>it.each</code> to loop through all possible parameter combinations:</p><pre><code class="js language-js">it.each(testParams)(
|
||
"should return page for deletion from %s",
|
||
async ({ preview, isInternal, bucket, key }) => {
|
||
await deletePageFromS3("url", preview, isInternal);
|
||
const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
|
||
expect(deleteObjectCommand.input).toEqual({
|
||
Bucket: bucket,
|
||
Key: key,
|
||
});
|
||
}
|
||
);</code></pre><p>This uses the <code>%s</code> variable to print the parameters from each test, which outputs:</p><pre><code class="language-none"> ✓ should return page for deletion from {
|
||
preview: false,
|
||
isInternal: false,
|
||
bucket: 'ipages-live',
|
||
key: 'url/index.html'
|
||
} (1 ms)
|
||
✓ should return page for deletion from {
|
||
preview: true,
|
||
isInternal: false,
|
||
bucket: 'staff.com-preview',
|
||
key: 'preview/url/index.html'
|
||
}</code></pre></div></article><nav class="ui attached segment deemphasized bottomPane" id="neuron-tags-pane"><div><span class="ui basic label zettel-tag" title="Tag">javascript</span><span class="ui basic label zettel-tag" title="Tag">jest</span><span class="ui basic label zettel-tag" title="Tag">testing</span></div></nav><nav class="ui bottom attached icon compact inverted menu blue" id="neuron-nav-bar"><!--replace-start-9--><!--replace-end-9--><a class="right item" href="impulse.html" title="Open Impulse"><i class="wave square icon"></i></a></nav></div></div><!--replace-end-6--><!--replace-end-3--><!--replace-end-2--><div class="ui center aligned container footer-version"><div class="ui tiny image"><a href="https://neuron.zettel.page"><img alt="logo" src="https://raw.githubusercontent.com/srid/neuron/master/assets/neuron.svg" title="Generated by Neuron 1.9.35.3" /></a></div></div></div></body></html> |