2
\$\begingroup\$

I've recently been learning and adopting Promises, async/await, and unit testing for my JavaScript server project. I just finished a unit test for some pubsub code that was more convoluted than anything I'd done before, and I feel like I'm missing some idiom or pattern that could make it a little simpler.

Context

I'm using Google Pub/Sub API. I wrote publish and listen wrappers, and wrote a unit test to exercise both in tandem - mostly because testing either in isolation is essentially impossible, since I want to test correct usage of the real API, not mocks. (The logic is simple, it's the correct API usage that is in question and must be verified.)

The Wrappers

pubsub.js

const pubsub = require('@google-cloud/pubsub')()

module.exports = (log) => ({

    listen: function (subscription, message_handler) {
        const sub = pubsub.subscription( subscription )

        const error_handler = error => log.error(error)
        sub.on( 'message', message_handler )
        sub.on( 'error',   error_handler   )

        return () => {
            sub.removeListener( 'message', message_handler )
            sub.removeListener( 'error',   error_handler   )
        }
    },

    publish: async function (topic, data) {
        const [ ids ] = await pubsub.topic(topic).publish( data )
        return ids[0]
    },

    simple_handler: function ( processor ) {
        return async (message) => {
            await processor(message)
            message.ack()
        },
    }

}

The Test

test_pubsub.js

test( 'test__listen_and_publish', async t => {
    const data = 'foo'
    return new Promise( async (resolve, reject) => {
        try {
            let published_id_p, stop_listening

            const processor = async (message) => {
                const published_id = await published_id_p
                if ( message.id === published_id ) {
                    t.is( message.data, data )
                    stop_listening()
                    resolve()
                }
            }
            const handler = pubsub.simple_handler( processor, { name: 'listen_and_publish' } )

            stop_listening = pubsub.listen(  'test-sub', handler )
            published_id_p = pubsub.publish( 'test',     data    )
        } catch (error) { reject(error) }
    })
})

The code works. It listens for messages until it gets the test one it sent, in case there are leftover unacknowledged messages from previous failed test runs. Once it receives the test message, it resolves the promise which ends the test successfully.

The code is ugly, though. I'm creating callbacks that refer to variables I've declared already (so they're in context) but won't initialize until several lines later.

BTW - This is my first time creating a Promise, rather than building on one returned to me for a library. I didn't know how else to wire up test success to the receiving of the correct message.

PS - My unit test framework is AVA.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.