Logo

dev-resources.site

for different kinds of informations.

Level Up Your Node.js Testing with Native Test Runner and Mocks: A BigQuery Example

Published at
7/25/2024
Categories
node
testing
mocking
javascript
Author
chetanppatil
Categories
4 categories in total
node
open
testing
open
mocking
open
javascript
open
Author
12 person written this
chetanppatil
open
Level Up Your Node.js Testing with Native Test Runner and Mocks: A BigQuery Example

Code and Test

In today's software development, testing is not just good practice, it's necessary, in my opinion.

I'm excited to share some valuable insights on testing in Node.js! Through my own experiences, I've discovered some practical tips and techniques that I believe will be helpful to the developer community.

Before Node.js 20, developers relied on external testing tools like Jest and Mocha to ensure code quality. But with the arrival of Node.js 20, the game changed. This tutorial will explore the exciting new world of Node.js's built-in test runner, exploring powerful features like mocks.

Native Node.Js Test Runner Offers:

  • Simplicity (Easy setup, no extra depenndency)
  • Integration (Seamless integration with Node.js features)
  • Efficiency (Fast execution)

"Node.js's native test runner simplifies ESM testing and mocking, making it a better choice over Jest for modern projects."


From Theory to Practice: Mocking BigQuery Calls in Node.Js

We covered fundamentals of Node.Js native test runner, now let's get our hands dirty with real world scenario.

In this practical example, we'll walk through how to leverage the node:test module and its mocking capabilities to thoroughly test a Node.js module responsible for interacting with Google BigQuery. We'll focus on the @google-cloud/bigquery library and demonstrate how to simulate its behavior, ensuring our code is resilient and functions correctly without making any real BigQuery calls during testing.

Creating BigQuery Service

Let's assume to have task about querying data from BigQuery dataset in stream mode.



import { BigQuery } from '@google-cloud/bigquery';

export const createBigQueryClient = () => new BigQuery();

const query = async ({ query, emailId, country }, bigquery = createBigQueryClient()) => {
  const output = [];

  const options = {
    query: query,
    params: {
      emailId,
      country,
    },
  };

  return new Promise((resolve, reject) => {
    bigquery.createQueryStream(options)
      .on('error', reject)
      .on('data', (row) => {
        output.push(row);
      })
      .on('end', () => {
        resolve(output); // Resolve the promise when the stream ends
      });
  });
}

export {
  query,
};


Enter fullscreen mode Exit fullscreen mode

In this code, you must have observed export for createBigQueryClient.

Why Export createBigQueryClient?

Wondering why there is such simple function export:



export const createBigQueryClient = () => new BigQuery();


Enter fullscreen mode Exit fullscreen mode

Here's the reasoning:

Testability: This function is key to easily mocking the BigQuery client in tests. By abstracting the creation of the client, it becomes easy to swap it out for a mock during testing.

Potential Configuration: While not used in this example, imagine needing to pass authentication option or other setting. Having this function makes such future cases easy for implementation without changing core login for query function.

Soften BigQuery with Mocks: The Tests

Testing bigquery.js service directly would require live BigQuery project running, which is costly and not always controllable. Here mocking comes into picture and Node.Js native test runner gives us that functionality in simplest way.

The Power of node:test



import assert from 'node:assert';
import { describe, it, beforeEach, after, mock } from 'node:test';
import { query } from './bigquery.js';

describe('BigQuery Module', () => {
  let mockBigQuery;

  beforeEach(() => {
    mockBigQuery = {
      createQueryStream: mock.fn(() => ({
        on: mock.fn(function (event, callback) {
          if (event === 'error' && mockBigQuery.error) {
            // @ts-ignore
            callback(mockBigQuery.error);
          } else if (event === 'data' && mockBigQuery.data) {
            mockBigQuery.data.forEach(callback); // Stream sends individual objects
          } else if (event === 'end') {
            // @ts-ignore
            callback();
          }
          return this; // Return 'this' for chaining

        }),
      })
      ),
    };
  });

  after(() => {
    mock.restoreAll();
  });

  const tests = {
    success: [
      {
        name: 'should execute a query and return results',
        input: {
          query: 'SELECT * FROM dataset.table WHERE emailid = @emailid AND country = @country',
          emailid: '[email protected]',
          country: 'IN'
        },
        mockData: [{ id: 1, name: 'Chetan' }],
        expectedOutput: [{ id: 1, name: 'Chetan' }],
        expectedError: null,
      },
      {
        name: 'should handle empty result set',
        input: {
          query: 'SELECT * FROM `project.dataset.table` WHERE 1=2', // Always false condition
          emailid: '[email protected]',
          country: 'CN',
        },
        mockData: [],
        expectedOutput: [],
        expectedError: null,
      },
    ],
    error: [
      {
        name: 'should reject the promise on BigQuery error',
        input: {
          query: 'INVALID SQL QUERY',
          emailId: '[email protected]',
          country: 'AT',
        },
        mockData: null,
        expectedOutput: null,
        expectedError: new Error('Simulated BigQuery Error'),
      },
    ],
  };

  tests.success.forEach((t) => {
    it(t.name, async () => {
      mockBigQuery.data = t.mockData;

      const result = await query(t.input, mockBigQuery);
      assert.deepStrictEqual(result, t.expectedOutput);
    });
  });

  tests.error.forEach((t) => {
    it(t.name, async () => {
      mockBigQuery.error = t.expectedError;

      await assert.rejects(async () => {
        await query(t.input, mockBigQuery);
      }, {
        message: t.expectedError.message,
      });
    });
  });
});


Enter fullscreen mode Exit fullscreen mode

Key element

mock.fn(): A powerful tool from node:test to create mock functions. We use it to stub out the createQueryStream method of the BigQuery client.

Mock implementation of createQueryStream simulates data streaming and errors through callbacks.

"Using a table-driven test format helps minimize code duplication and efficiently manage new test scenarios."

Running the Tests

Executing tests is as simple as running the following command in terminal:



node --test


Enter fullscreen mode Exit fullscreen mode

Node.js will automatically discover and execute test files withing project.
The output looks like:

node --test

Why Node.Js Native Test Runner?

Built-in Goodness: Embrace the simplicity of using Node.js's native testing capabilities.

Streamlined Workflow: No need for external dependencies, making your project setup cleaner.

Improved Readability: node:test encourages well-structured tests that are easy to understand and maintain.

Future-Proofing: Align your testing practices with the future of Node.js development.

Time to Test Smarter, Not Harder

This article aimed to introduce you to the Node.js native test runner and demonstrate how to write test functions with straightforward mocking, similar to other testing frameworks.

Give native testing a try in your next project and see how easy it is to write clean, efficient tests with built-in mocking. Your code will be more reliable and easier to maintain.

I'm passionate about learning, sharing and helping developers, so I've started a collection of Node.js native test runner examples at https://github.com/TheCodersStudio/node-native-test-runner.

Happy coding!

mocking Article's
30 articles in total
Favicon
4. Testing (async) searchParams with Jest in Next 15
Favicon
Enhancing API Workflows: A Comprehensive Guide to Mock Responses and Efficient Testing
Favicon
Dummy API for Testing: How to Use It vs Mocking
Favicon
How Better Mocking Makes Building APIs Smoother and Speed Up Development
Favicon
A Step-by-Step Guide to API Creation and Mocking for Faster Development
Favicon
How I've been trying to improve mocking with Zod
Favicon
How to Set Up a Mock Server
Favicon
Best API Mocking Platforms in 2024
Favicon
Server Side Mocking for Playwright in NextJS (App Router) using Mock Service Worker
Favicon
How Mocking Against a Public API Endpoints within Blackbird Gets your API in Production Faster
Favicon
10 Best API Mocking Tools (2025 Review)
Favicon
Introduction to Jest: Unit Testing, Mocking, and Asynchronous Code
Favicon
Mocking an AI Chatbot API with Blackbird
Favicon
Incredibly Useful WireMock with IntelliJ IDEA
Favicon
Simplifying Mocking APIs for Faster Development: Basic & Advanced Techniques
Favicon
The Case Against Mocking Libraries
Favicon
Mocking an AI Chatbot API with Blackbird
Favicon
API Mocking: A Comprehensive Guide
Favicon
Mock TypeORM Package
Favicon
Level Up Your Node.js Testing with Native Test Runner and Mocks: A BigQuery Example
Favicon
Unit Tests for Frontend Developers [Part 2]
Favicon
Revamped Mock-API.net: Simplifying API Mocking for Developers
Favicon
Mocking with Sinon.js: A Comprehensive Guide
Favicon
Streamline Development with Effective API Mocking
Favicon
API mocking with Mock Service Worker + Vue.js
Favicon
Partial: how NOT to mock the whole world
Favicon
Introducing Mockallan: Testing with API-Level Stubs and Mocks
Favicon
Como fazer Deploy de uma api com Json-Server
Favicon
What's the real ROI of writing Mocks?
Favicon
Unleashing GraphQL Type-Based Mocking

Featured ones: