DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • Alexa Skill With Local DynamoDB
  • Mock the File System
  • How to Banish Anxiety, Lower MTTR, and Stay on Budget During Incident Response
  • How Node.js Works Behind the Scenes (HTTP, Libuv, and Event Emitters)

Trending

  • How to Build Your First Generative AI App With Langflow: A Step-by-Step Guide
  • The Battle of the Frameworks: Choosing the Right Tech Stack
  • Advanced gRPC in Microservices: Hard-Won Insights and Best Practices
  • Designing Microservices Architecture With a Custom Spring Boot Starter and Auto-Configuration Framework
  1. DZone
  2. Coding
  3. JavaScript
  4. Unit Testing Self-Invoking JavaScript Functions

Unit Testing Self-Invoking JavaScript Functions

Using QUnit and Sinon.js achieves the possibility of unit testing — and using the Google closure compiler with hard optimization enabled minifies code efficiently.

By 
Bendix Saeltz user avatar
Bendix Saeltz
·
Mar. 03, 17 · Opinion
Likes (0)
Comment
Save
Tweet
Share
12.0K Views

Join the DZone community and get the full member experience.

Join For Free

When Akanoo started out, we only tested our components run on Java — either written in Scala or using Grails or Groovy — with JUnit, ScalaTest, and Spock. By the end of 2015, we also wanted unit tests for our JavaScript tracking library that is integrated in our client’s online shops.

Finding the Right Test Framework

Nobody in our team had experience in testing JavaScript, so I went out looking for JavaScript unit test frameworks. What does one do if one has no clue? Google, of course. The first hit was QUnit, developed and used by the jQuery team. I looked into other libraries but decided to give QUnit a shot. The reasons I chose it were:

  • Support by the community (in this case, the jQuery community).
  • Used by some big players (jQuery certainly is a big player.)
  • Easy to use (check out the cookbook).
  • Easy to understand for Java developers (hence, similarity with JUnit).
  • Plug-in support and availability of plug-ins (we’ll come to this later).

So, I had chosen my test framework. Let’s write some tests.

Testing Self-Invoking JavaScript Functions

Well, testing wasn’t that easy at the beginning. Our tracking script is encapsulated in a self-invoking function also known as immediately-invoked function expression (IIFE). The reason behind this is to avoid polluting the global space (everything in the window object) with our functions and variables and only allow access via our API calls using the exposed function at(). See an example of an immediately-invoked function expression below:

<script>
    (function(a, b) {
        // our tracking code goes here
        var foo = "bar";

        function bla() {
            // sample function
            console.log(foo);
        }
    })(window, document);
</script>

As the name says, the function is immediately invoked after definition and I have no chance to call the function itself or to access the variables and functions inside. What could I do to test the function bla() in the above example?

My first idea was to comment out the two lines that define the function as self-invoking and define the parameterized variables by hand:

<script>
    var a = window,
        b = document; //(function(a, b) {
    // our tracking code goes here
    var foo = "bar";

    function bla() {
        // sample function
        console.log(foo);
    }
    //})(window, document);
</script>

Spying With Sinon.js

I finished writing some easy test checking for the output of functions with various input parameters. Unfortunately, some functions are called inside other functions — and these make for harder testing. If I want to know whether the inner function has been called, I need to spy on its execution.

<script>
    function A() {
        B();
    }
    function B() {
        // do something
    }
    A();
</script>

QUnit itself doesn’t offer functions for spying, so I googled again to find a solution for JavaScript mocking. I stumbled upon Sinon.js, which offers spies and stubs and integrates nicely with QUnit.

I started to write some tests, but the function sinon.spy (object, “function”) requires us to specify the encapsulating object of the “function” to spy. After my changes to the self-invoking function expression, the functions reside in the “global” scope, which, in JavaScript, means that they lie under the window object.

// the following line will be replaced by the code in the comment
var a = window,
    b = document; //(function(a, b) {
// our tracking code goes here
var obj = {
    // variables go here
    foo: "bar";
}

// functions go like this
obj.bla = function() {
    // sample function
    console.log(foo);
}
//})(window, document);

Unfortunately, I wasn’t able to spy functions using Sinon on the window object. So, I went on putting all functions inside my faked IIFE inside an object to make them testable. Of course, I had to refactor the existing code already to some extent, i.e., extracting functions, avoiding anonymous functions, etc. I strongly recommend using JSHint.

Notice: When asserting spies with Sinon’s function with Args (arg0, arg1, and so on), be aware that the order of arguments must be the same as in the call. If you want to check for the first two of three arguments, you may omit the third but if you want to check for the latter ones, the first argument needs to be defined.

Stubbing With Sinon.js

Sometimes, the result of function A is dependent on the result of function B that is called inside function A. To test, I have to manipulate the inner function B to always return a predefined value. This is called stubbing and can be done with Sinon.js by calling sinon.stub(object, “function”). You should also check out the faked XHR and faked server that Sinon offers. They were very helpful for my unit tests.

Conclusion

If you’ve read until now, you certainly can guess that it was a long way down the road to 95% test coverage (measured with JSCover). The upsides of this solution using QUnit and Sinon.js is apparently that we achieved the possibility of unit testing. The obvious downside is the superfluous object in the IIFE. That is, however, not so bad, as the Google closure compiler with hard optimization enabled minifies the code efficiently.

unit test JavaScript

Published at DZone with permission of Bendix Saeltz. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Alexa Skill With Local DynamoDB
  • Mock the File System
  • How to Banish Anxiety, Lower MTTR, and Stay on Budget During Incident Response
  • How Node.js Works Behind the Scenes (HTTP, Libuv, and Event Emitters)

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: