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

How does AI transform chaos engineering from an experiment into a critical capability? Learn how to effectively operationalize the chaos.

Data quality isn't just a technical issue: It impacts an organization's compliance, operational efficiency, and customer satisfaction.

Are you a front-end or full-stack developer frustrated by front-end distractions? Learn to move forward with tooling and clear boundaries.

Developer Experience: Demand to support engineering teams has risen, and there is a shift from traditional DevOps to workflow improvements.

Related

  • Driving DevOps With Smart, Scalable Testing
  • Unit Testing Large Codebases: Principles, Practices, and C++ Examples
  • Practical Use of Weak Symbols
  • Generate Unit Tests With AI Using Ollama and Spring Boot

Trending

  • Multiple File Upload is Easy in HTML5
  • Kafka Architecture
  • Building a Scalable ML Pipeline and API in AWS
  • When Agile Teams Fake Progress: The Hidden Danger of Status Over Substance
  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.

By 
Cliff Hall user avatar
Cliff Hall
·
Updated Mar. 16, 17 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
24.6K 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.

Related

  • Driving DevOps With Smart, Scalable Testing
  • Unit Testing Large Codebases: Principles, Practices, and C++ Examples
  • Practical Use of Weak Symbols
  • Generate Unit Tests With AI Using Ollama and Spring Boot

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: