<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Unit Testing on Stuart Forrest</title><link>https://www.uglydirtylittlestrawberry.co.uk/tags/unit-testing/</link><description>Recent content in Unit Testing on Stuart Forrest</description><generator>Hugo -- gohugo.io</generator><language>en-uk</language><lastBuildDate>Sun, 19 Sep 2021 15:10:12 +0000</lastBuildDate><atom:link href="https://www.uglydirtylittlestrawberry.co.uk/tags/unit-testing/" rel="self" type="application/rss+xml"/><item><title>Unit testing Auth0 custom database Action Scripts</title><link>https://www.uglydirtylittlestrawberry.co.uk/posts/unit-testing-auth0-database-action-scripts/</link><pubDate>Sun, 19 Sep 2021 15:10:12 +0000</pubDate><guid>https://www.uglydirtylittlestrawberry.co.uk/posts/unit-testing-auth0-database-action-scripts/</guid><description>&lt;p>I recently wrote about &lt;a href="https://www.uglydirtylittlestrawberry.co.uk/posts/pretty-urls-cloudfront-functions/">unit testing Cloudfront Functions&lt;/a> which presented some challenges. This post is about unit testing &lt;a href="https://auth0.com/">Auth0&lt;/a> Action Scripts when using a custom database, which also presented some similar challenges but had a different solution.&lt;/p>
&lt;h2 id="what--is-auth0">What is Auth0?&lt;/h2>
&lt;p>&lt;a href="https://auth0.com/">Auth0&lt;/a> is an Identity-as-a-service (IaaS) provider.&lt;/p>
&lt;h3 id="what-is-a-custom-database-and-when-would-i-use-one">What is a custom database and when would I use one?&lt;/h3>
&lt;p>When using Auth0 there is the facility to create one or more user databases that store users authenticating with a username/password combination (&lt;a href="https://auth0.com/docs/connections/database">link&lt;/a>). Should you need to, these stores can be connected to an external database that (presumably) you already control. A custom database is a database not controlled by Auth0, likely a SQL database but it doesn&amp;rsquo;t actually matter - it doesn&amp;rsquo;t really have to be a database at all, just return users.&lt;/p>
&lt;p>The most likely use for a custom database is probably going to be lazily migrating users from an old identity provider to Auth0. Lazy migration will allow users to be moved over to Auth0 at the point of login without needing to change their password (&lt;a href="https://auth0.com/docs/users/import-and-export-users/configure-automatic-migration-from-your-database">link&lt;/a>). Another use for a custom database is to not use Auth0 for storing users at all (&lt;a href="https://auth0.com/docs/connections/database/custom-db/overview-custom-db-connections">link&lt;/a>) but just to handle the authentication flows, I would expect this to be a less common usage but Auth0 facilitate it.&lt;/p>
&lt;h3 id="what-is-an-action-script-and-why-are-they-are-hard-to-unit-test">What is an Action Script and why are they are hard to unit test?&lt;/h3>
&lt;p>An Action Script is some custom code that is deployed into Auth0, it will be invoked under certain scenarios (eg. when a user logs in, &lt;a href="https://auth0.com/docs/connections/database/custom-db/templates">link&lt;/a>). There are various points at which Auth0 functionality can be extended/augmented in this way (&lt;a href="https://auth0.com/docs/rules">rules&lt;/a>, &lt;a href="https://auth0.com/docs/hooks">hooks&lt;/a>, &lt;a href="https://auth0.com/docs/actions">actions&lt;/a>) and they all behave in slightly different ways (it&amp;rsquo;s a little frustrating). This post focuses on Action Scripts in the context of using a custom database.&lt;/p>
&lt;p>The runtime in which these Action Scripts execute has some restrictions that your code must operate within (&lt;a href="https://auth0.com/docs/connections/database/custom-db/templates#limits">link&lt;/a>). The relevant (and undocumented) one for this post is that top-level &lt;code>exports&lt;/code> are not allowed. This makes structuring your code to facilitate unit testing a little tricky.&lt;/p>
&lt;p>Effectively, neither of the following approaches will work:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">export&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">handler&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// do something here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">handler&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The first approach using &lt;code>export&lt;/code> will cause a syntax error and trying to access &lt;code>module&lt;/code> will fail.&lt;/p>
&lt;h2 id="an-approach-to-unit-testing-them-auth0-custom-database-action-scripts">An approach to unit testing them Auth0 custom database Action Scripts&lt;/h2>
&lt;p>My approach to &lt;a href="https://www.uglydirtylittlestrawberry.co.uk/posts/pretty-urls-cloudfront-functions/">testing Cloudfront Functions&lt;/a> using the &lt;a href="https://github.com/jhnns/rewire">rewire module&lt;/a> is a possible solution to unit testing Auth0 custom database Action Scripts. It is not the one I used though, this is because if I can get away with not bringing in additional dependencies then that is desirable. Rewire is also a pretty invasive module that (I assume) fiddles with the inner workings of node&amp;rsquo;s module resolution process, which is a bit much if we don&amp;rsquo;t have to do it.&lt;/p>
&lt;p>Whilst top-level &lt;code>exports&lt;/code> are not permitted and &lt;code>module&lt;/code> is undefined when accessed at the top-level, &lt;code>module&lt;/code> is, surprisingly, available outside of the global scope. This is a perfect use case for an &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE">Immediately Invoked Function Expressions&lt;/a> (IIFE). An IIFE is essentially a function that is run immediately after it has been defined but bounded in &lt;code>()&lt;/code> that isolates its scope from the outer scope. This means it will be immediately invoked when the module is imported into the runtime which Auth0 has to do at some point to execute the code.&lt;/p>
&lt;p>Taking advantage of these Javascript capabilities my first pass at writing a unit-testable Action Script looked something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>(() =&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">doStuff&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">console&lt;/span>.&lt;span style="color:#a6e22e">log&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;handler ran&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#a6e22e">module&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">doStuff&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#a6e22e">doStuff&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>})()&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The thinking was that because &lt;code>module&lt;/code> is not available in the global scope, it isn&amp;rsquo;t available in the runtime at all. To account for this the &lt;code>exports&lt;/code> are only set if &lt;code>module&lt;/code> is defined, this means that when my unit tests attempt to import this module &lt;code>doStuff&lt;/code> is importable. But when the module is executed in the Auth0 runtime trying to manipulate exports shouldn&amp;rsquo;t cause a runtime error and &lt;code>doStuff&lt;/code> is available as an (almost) top level function.&lt;/p>
&lt;p>This worked, &lt;code>handler ran&lt;/code> is logged to the terminal in Auth0 when I tested it. Job done? Yes, and no. I pushed it a bit further and removed the conditional testing of &lt;code>module&lt;/code> like so:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>(() =&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">doStuff&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">console&lt;/span>.&lt;span style="color:#a6e22e">log&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;handler ran&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">doStuff&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#a6e22e">doStuff&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>})()&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>I was very surprised when this worked, there were no runtime errors when I tested it in Auth0 and &lt;code>handler ran&lt;/code> was logged to the terminal. I&amp;rsquo;m not really sure what is going on here, &lt;code>module&lt;/code> not being available in the runtime is inconvenient but consistent. Whereas, &lt;code>module&lt;/code> just not being available in the global scope is a little weird.&lt;/p>
&lt;p>Auth0 do have a keen interest in securing the runtime for these custom database Action Scripts which is awesome, for example they vet third party modules to include in the runtime (&lt;a href="https://auth0.com/docs/best-practices/custom-database-connections-scripts/environment#npm-modules">link&lt;/a>). Perhaps the behaviour witnessed is security related and they didn&amp;rsquo;t want exporting to be allowed (&lt;a href="https://www.uglydirtylittlestrawberry.co.uk/posts/pretty-urls-cloudfront-functions/">a la Cloudfront Functions&lt;/a>), perhaps &lt;code>module&lt;/code> being available in a nested scope is not intentional. It is though, and in this instance I took advantage of it.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Auth0 custom database Action Scripts exhibit some slightly odd runtime behaviour but with a little experimentation and taking advantage of some not always very useful Javascript features like IIFEs unit testing them was doable. Structuring your code a certain way to facilitate unit testing, as was the case here, I don&amp;rsquo;t consider a bad thing. Code that is hard to test is usually too complex or unobservable, if unit testing code helps to reduce the complexity, break it up or make it more observable then I consider that a good thing.&lt;/p>
&lt;h3 id="get-in-contact">Get in contact&lt;/h3>
&lt;p>If you have comments, questions or better ways to do anything that I have discussed in this post then please get in contact via &lt;a href="https://linkedin.com/in/stuart-f-41a43b180">LinkedIn&lt;/a> or &lt;a href="mailto:stuart@uglydirtylittlestrawberry.co.uk">email&lt;/a>.&lt;/p></description></item><item><title>Unit testing Cloudfront Functions</title><link>https://www.uglydirtylittlestrawberry.co.uk/posts/unit-testing-cloudfront-functions/</link><pubDate>Fri, 10 Sep 2021 07:13:47 +0000</pubDate><guid>https://www.uglydirtylittlestrawberry.co.uk/posts/unit-testing-cloudfront-functions/</guid><description>&lt;p>I&amp;rsquo;ve recently taken an interest in Cloudfront Functions and started using them for some of this sites functionality. To read more about what I&amp;rsquo;ve been doing check out these posts that capture my experience so far:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.uglydirtylittlestrawberry.co.uk/posts/pretty-urls-cloudfront-functions/">Hugo pretty URLs - migrating from Lambda@Edge to Cloudfront Functions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.uglydirtylittlestrawberry.co.uk/posts/basic-auth-cloudfront-functions/">HTTP Basic Auth with Cloudfront Functions&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>This post is about my approach to unit testing the Cloudfront Function code which was a little challenging.&lt;/p>
&lt;h2 id="what-are-cloudfront-functions">What are Cloudfront Functions?&lt;/h2>
&lt;p>&lt;a href="https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/">Cloudfront
Functions&lt;/a>,
whilst not a successor to Lambda@Edge, are an evolution of them. Whereas Lambda@Edge allows fully fledged lambda
functions to run within a regional edge cache (AWS region) closest to the request origin, Cloudfront Functions run right
at the edge where the content is ideally going to be served from. In terms of Cloudfront architecture you cannot get
closer to the user. There are some &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html">restrictions to Cloudfront
Functions&lt;/a>
you have to bear in mind like having a limited runtime, execution time, memory allowance, package size and that they are
javascript only with no network access.&lt;/p>
&lt;h2 id="why-are-cloudfront-functions-hard-to-unit-test">Why are Cloudfront Functions hard to unit test?&lt;/h2>
&lt;p>Because there are no exports allowed. There are a bunch of restrictions within the Cloudfront Function runtime as highlighted, but it is the lack of any kind of export support that makes it difficult. This will work:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">handler&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// do something here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>but any attempt at exporting will cause a runtime error when the Cloudfront Function is run. So anything akin to the following wont work:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">export&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">handler&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// do something here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">handler&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Even this wont work, which surprised me:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">export&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">handler&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// do something here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#a6e22e">module&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">handler&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If you cannot export a function (or import one) how can you test it in another file using a library like &lt;a href="https://jestjs.io/">jest&lt;/a>? Ordinarily you don&amp;rsquo;t want to test functions that are not exported because the public interface of a module (its exported functions) are the testing target - testing anything else becomes brittle and tests implementation as opposed to behaviour. I&amp;rsquo;ve not wanted to be able to this before but Cloudfront Functions are my special case!&lt;/p>
&lt;h3 id="what-are-the-options">What are the options?&lt;/h3>
&lt;p>I considered a few approaches to facilitate unit testing my Cloudfront Functions that I realised wouldn&amp;rsquo;t work or I didn&amp;rsquo;t like for various reasons:&lt;/p>
&lt;ol>
&lt;li>Exporting a string template of the function body and then creating a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function">&lt;code>Function&lt;/code> object&lt;/a> with it in both my Cloudfront Function at runtime and in my tests. Whilst this would work great for the testing part the &lt;code>Function&lt;/code> class is not available in the Cloudfront Function runtime. Even if it worked there would be no support in VSCode for syntax highlighting etc. making the development experience unpleasant.&lt;/li>
&lt;li>The function code was being placed inline in my Cloudformation template as required (covered &lt;a href="https://www.uglydirtylittlestrawberry.co.uk/posts/pretty-urls-cloudfront-functions/">here&lt;/a>). This meant I could use &lt;code>sed&lt;/code> or some &lt;code>regex/replacing&lt;/code> to remove any export statements. This would have worked but felt too brittle and I also thought it would be confusing to mix supported and unsupported features in the file without much context.&lt;/li>
&lt;li>Duplicate the code both in the Cloudformation template and again in a test file. This is possible but ensuring that when one is updated so is the other will inevitably lead to them going out of sync at some point even with the best intentions.&lt;/li>
&lt;li>Use &lt;code>babel&lt;/code> to transpile the code to something supported, unfortunately no form of exporting &lt;strong>at all&lt;/strong> is allowed.&lt;/li>
&lt;li>Don&amp;rsquo;t unit test it. This would work but if not unit testing something is the easy option (when isn&amp;rsquo;t it?!) it should probably be unit tested as there is some inherent complexity or lack of observability there. Also, it was a challenge by now that I wanted to solve and would undoubtedly learn something along the way.&lt;/li>
&lt;/ol>
&lt;h2 id="an-approach-to-unit-testing-cloudfront-functions">An approach to unit-testing Cloudfront Functions&lt;/h2>
&lt;p>First I came across the &lt;a href="https://github.com/jhnns/rewire">rewire&lt;/a> module but that only supports up to ES5 syntax and Cloudfront Functions blends syntax/features support from ES5, ES6, ES7 and beyond. Looking further afield I found a &lt;code>babel&lt;/code> plugin called &lt;a href="https://github.com/speedskater/babel-plugin-rewire">babel-plugin-rewire&lt;/a> that brings the approach of &lt;code>rewire&lt;/code> a bit more up to date.&lt;/p>
&lt;p>My &lt;code>babel&lt;/code> config looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">module&lt;/span>.&lt;span style="color:#a6e22e">exports&lt;/span> &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">presets&lt;/span>&lt;span style="color:#f92672">:&lt;/span> [[&lt;span style="color:#e6db74">&amp;#34;@babel/preset-env&amp;#34;&lt;/span>, { &lt;span style="color:#a6e22e">targets&lt;/span>&lt;span style="color:#f92672">:&lt;/span> { &lt;span style="color:#a6e22e">node&lt;/span>&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#e6db74">&amp;#34;current&amp;#34;&lt;/span> } }]],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Only invoke rewire when tests are running
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">plugins&lt;/span>&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#a6e22e">process&lt;/span>.&lt;span style="color:#a6e22e">env&lt;/span>.&lt;span style="color:#a6e22e">NODE_ENV&lt;/span> &lt;span style="color:#f92672">===&lt;/span> &lt;span style="color:#e6db74">&amp;#34;test&amp;#34;&lt;/span> &lt;span style="color:#f92672">?&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;rewire&amp;#34;&lt;/span>] &lt;span style="color:#f92672">:&lt;/span> [],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>};&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>I only want the plugin to run in a test environment, it takes quite invasive actions and I don&amp;rsquo;t want the possible side effects of that anywhere near production code. If I had more things to test (I don&amp;rsquo;t) I could also target the plugin only at my Cloudfront Function test files.&lt;/p>
&lt;p>And my test setup looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#a6e22e">myModule&lt;/span> &lt;span style="color:#a6e22e">from&lt;/span> &lt;span style="color:#e6db74">&amp;#34;./myModule&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">describe&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;myModule tests&amp;#34;&lt;/span>, () =&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">it&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;should do some stuff&amp;#34;&lt;/span>, () =&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Arrange
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// Get unexported function
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">unexportedFunc&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">myModule&lt;/span>.&lt;span style="color:#a6e22e">__get__&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;functionName&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Act
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">res&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">unexportedFunc&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Assert some stuff
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> });
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>});&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h3 id="the-benefits">The benefits&lt;/h3>
&lt;p>Though the Cloudfront Function runtime still restricts the syntax and features that I use this &lt;a href="https://github.com/speedskater/babel-plugin-rewire">babel-plugin-rewire&lt;/a> gives me a decent amount more flexibility with how I can write the functions.&lt;/p>
&lt;p>Using the approach of &lt;code>rewire&lt;/code> means that I have what is hopefully a reliable and supported method of testing my functions in this way. When reading around I came across suggestions of reading the string contents of the file and using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval">eval&lt;/a> or Node&amp;rsquo;s &lt;a href="https://nodejs.org/api/vm.html">vm&lt;/a> to run it. This seems brittle, makes test setup pretty complex and are harder to reason about when I come back to the test code.&lt;/p>
&lt;h3 id="some-downsides">Some downsides&lt;/h3>
&lt;p>Babel had to be added to the project which is considerably more complex than adding the average dependency. Ideally that wouldn&amp;rsquo;t have been required, particularly as it is only being used by &lt;code>jest&lt;/code> to run tests. Having said that, &lt;code>babel&lt;/code> wasn&amp;rsquo;t really a necessity as I could have written my functions in entirely ES5 syntax but I didn&amp;rsquo;t want to. I value the developer experience as well as the speed and ease with which I can make changes to the site as required, this felt like the best way to facilitate both of those things.&lt;/p>
&lt;p>Not all functions can be accessed even when using this approach and I don&amp;rsquo;t know why. The only mention I&amp;rsquo;ve found of this is &lt;a href="https://github.com/speedskater/babel-plugin-rewire/issues/235">this issue&lt;/a> but there is no official response or progress. If super simple/light functions are untestable they are hopefully simple enough that the value of unit testing them is minimal. Testing them by testing other code that consumes them shouldn&amp;rsquo;t add too much complexity.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>It took some reading and thinking around the problem to figure out the best way to unit test my Cloudfront Functions but I&amp;rsquo;ve implemented a solution I&amp;rsquo;m happy with. I&amp;rsquo;ve not worked on any commercial projects that have utilised Cloudfront Functions yet but hopefully save some time with this approach than having to solve the same challenge.&lt;/p>
&lt;h3 id="get-in-contact">Get in contact&lt;/h3>
&lt;p>If you have comments, questions or better ways to do anything that I have discussed in this post then please get in contact via &lt;a href="https://linkedin.com/in/stuart-f-41a43b180">LinkedIn&lt;/a> or &lt;a href="mailto:stuart@uglydirtylittlestrawberry.co.uk">email&lt;/a>.&lt;/p></description></item></channel></rss>