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 Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
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
Partner Zones AWS Cloud
by AWS Developer Relations
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
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Frameworks
  4. Unit Testing with Jasmine and Async/Await

Unit Testing with Jasmine and Async/Await

In this article, you'll learn how to conduct asynchronous unit testing on your code using async/await, Jasmine, and NodeJS technologies.

Cliff Hall user avatar by
Cliff Hall
·
Mar. 16, 17 · Tutorial
Like (3)
Save
Tweet
Share
23.32K Views

Join the DZone community and get the full member experience.

Join For Free

Today, I wanted to write some unit tests against a Firebase database using Jasmine.

Connecting to Firebase from Node is just as easy as it is from a browser. I’ve done it before on a previous version of the Firebase SDK, so I didn’t expect a lot of problems there.

It was really just the butt-ugly tests I was worried about.

Testing asynchronous operations with Jasmine has always been supported; originally with the runs() and waitsFor() methods, which made for somewhat verbose tests, and later in 2.0 with done(), which is, IMHO, a clumsy promise + callback-to-move-on hack.

So, I decided to try a different approach. While async/await didn’t make it into ECMAScript 2016 (ES7), it is available in Chrome’s V8 as an experimental feature, and is supported in Node 7.6 without special flags. I’d been hearing a lot about it so I decided to give it a whirl. Spoiler alert: It’s totally frickin’ awesome!

Even a Simple Task Can Sometimes Be Made Simpler

Making asynchronous code look synchronous is a real trick, regardless of what language you’re trying to do it with. JavaScript Promises are a big help, but async/await comes closer than anything I’ve seen so far.

The basic premise looks like this:

async function fetchOrSupplyDefault(url) {
  let retval;
  try {
    retval = await fetch(url); 
  } catch(e) {
    retval = "Default Data";
  }
  return retval;
}

In the above function, we use the async keyword to indicate that the function will be performing one or more asynchronous operations. The  fetch() function returns a promise, so we would normally chain a then() call, passing a function to be called when the promise resolves, making things much more unreadable. Instead, we can prepend the await keyword, and when the promise resolves, the waiting variable retval will be set.

One thing to note here is that return from an async function will be wrapped in Promise.resolve and should be handled accordingly. Still, that’s already pretty slick. You couldn’t ask for more readable code inside this function.

Now Let’s Try That in a Test

The nice thing about using async/await inside Jasmine is that a try/catch already surrounds each test so that the framework can tally all the errors in the suite rather than crashing to a halt the first time it encounters one. That makes our code even simpler.

Note: Tip o' the propeller beanie to Joseph Zimmerman for pointing out in the comments that my original database test was flawed. The upshot was that I needed to add the jasmine-await npm package. For additional proof I've written the fetched data snapshot's uid property to stdout.

In the following test suite, we:

  1. Use the jasmine-await library. (It extends the functions it(), beforeEach(), afterEach(), beforeAll() and afterAll()and wraps them in the async() function. So you can always use await() to wait for a promise's resolution or rejection.)
  2. Use the firebase-admin node library to connect to a database using the serviceAccount.json file that can be downloaded from the Firebase console for the project. This happens in the call to beforeAll(), so that it is done once prior to running the tests. Straightforward stuff. Handy if you need to understand how to connect to Firebase, otherwise not much to linger on here.

  3. Test that the app was is initialized successfully. A basic, synchronous test.

  4. Test that async / await actually works with a simple example from the Mozilla docs.

  5. Test that a known user profile can be downloaded from Firebase. This is where the magic happens.

// Use the jasmine-await npm package
var async = require("jasmine-await");
var it = async.it;
var await = async.await;

describe("Test Firebase access with async/await", () => {
    let admin = require("firebase-admin");
    let serviceAccount = require(__dirname + "/serviceAccountKey.json");
    let app = null;

    // Initialize the app with loaded credentials
    beforeAll( () => {
        app = admin.initializeApp({
            credential: admin.credential.cert(serviceAccount),
            databaseURL: "https://mytestdatabse.firebaseio.com"
        });
    });

    // Test 1: Ensure we've got an initialized app
    it("receives an initialized app from firebase", () => expect(app).not.toBe(null) );

    // Test 2: Prove that async/await works
    it('tests that async / await works', async () => {
        function resolveAfter2Seconds(x) {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(x);
                }, 2000);
            });
        }

        async function add1(x) {
            var a = resolveAfter2Seconds(20);
            var b = resolveAfter2Seconds(30);
            return x + await a + await b;
        }

        var v = await add1(10);
        expect(v).toBe(60);
    });

    // Test 3: Read a known profile
    it("fetches a known profile from firebase", async () => {
        const uid = "1sT1mpFabkVJcXHtTCTsqiLiTrF3";
        let profileRef = admin.database().ref('/profile/' + uid);
        let snapshot = await profileRef.once('value');
        expect(snapshot).not.toBe(null);
        expect(snapshot.val().uid).toBe(uid);
        process.stdout.write(""+snapshot.val().uid); // For visual proof
    });
});

Running this test suite outputs:

Destiny:SineWav3.Client.Shell cliff$ jasmine
Started
..1sT1mpFabkVJcXHtTCTsqiLiTrF3.


3 specs, 0 failures
Finished in 3.034 seconds

Whoa! That Really Worked? Lets Break It to be Sure.

It was almost too easy. In order to convince myself that async/await was having the intended effect and that the magic wasn’t wrapped up in the Firebase Admin SDK, I removed the async and await keywords from the final test:

    // Test 3: Read a known profile
    it("fetches a known profile from firebase", () => {
        const uid = "1sT1mpFabkVJcXHtTCTsqiLiTrF3";
        let profileRef = admin.database().ref('/profile/' + uid);
        let snapshot = profileRef.once('value');
        expect(snapshot).not.toBe(null);
        expect(snapshot.val().uid).toBe(uid);
        process.stdout.write(""+snapshot.val().uid);
    });

Now the output looks like:

Destiny:SineWav3.Client.Shell cliff$ jasmine
Started
..F

Failures:
1) Test Firebase with async/await fetches a known profile from firebase
  Message:
    Failed: snapshot.val is not a function
  Stack:
    TypeError: snapshot.val is not a function
        at it (/Users/cliff/Documents/SineWav3.Client.Shell/spec/db-project-spec.js:49:25)
        at tryBlock (/Users/cliff/Documents/SineWav3.Client.Shell/node_modules/asyncawait/src/async/fiberManager.js:39:33)
        at runInFiber (/Users/cliff/Documents/SineWav3.Client.Shell/node_modules/asyncawait/src/async/fiberManager.js:26:9)

3 specs, 1 failure
Finished in 2.08 seconds

Conclusion

I am generally slow to get onboard with additions to JavaScript since I’d rather allow browsers to widely adopt first. But in the case of Node.js, I only have one JS engine to be concerned about. As long as I’m running a stable version of Node that supports a feature, I’m happy to consider it. In this case, I’m really glad I did, and I highly recommend you give it a try in your own scripts or unit tests. But keep in mind, it’s not something you want to fold into your production browser-based code just yet, since it’s still experimental, at least until ES8.

unit test Jasmine (JavaScript testing framework)

Published at DZone with permission of Cliff Hall, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 10 Most Popular Frameworks for Building RESTful APIs
  • Software Maintenance Models
  • LazyPredict: A Utilitarian Python Library to Shortlist the Best ML Models for a Given Use Case
  • All the Cloud’s a Stage and All the WebAssembly Modules Merely Actors

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: