Playing around with Segment Analytics testing using Jest in TypeScript

I've been struggling to write a unit test that verifies if the .track method of Analytics is being called. Despite my efforts, the test keeps failing, even though invoking the function through http does trigger the call. I'm unsure if I've incorrectly mocked it, or if there is another underlying issue?

index.ts:

import { Request } from "../types"
import { getSecret } from "../src/secrets"
import Analytics from "analytics-node"

const logger = (req: Request) => {
    const analytics = new Analytics(<string>process.env.WRITE_KEY);
    return analytics.track({
        userId: req.userId
    });
}

export default logger

index.test.ts:

jest.mock('analytics-node');
import { Request } from "../types"
import logger from "./index"
import Analytics from "analytics-node"

const mockAnalytics = new Analytics(process.env.WRITE_KEY = 'test');

describe('Logger tests', () => {
    it(`Should call analytics.track`, () => {
        const request: Request = {
            userId: 23
        }
        return logger(request).then(() => {
            expect(mockAnalytics.track).toHaveBeenCalled()
        });
    });
});

Answer №1

Utilizing the concept of Automatic mock involves invoking jest.mock('analytics-node').

By calling jest.mock('analytics-node'), you are provided with a convenient "automatic mock" that can be utilized to monitor calls to the class constructor and its methods. This action replaces the ES6 class with a mock constructor and substitutes all methods with mock functions that consistently yield undefined. The method calls can be accessed via

theAutomaticMock.mock.instances[index].methodName.mock.calls
.

Example:

index.ts:

import Analytics from 'analytics-node';

export interface Request {
  userId: string | number;
}

const logger = (req: Request) => {
  const analytics = new Analytics(<string>process.env.WRITE_KEY);
  return analytics.track({
    userId: req.userId,
    anonymousId: 1,
    event: '',
  });
};

export default logger;

index.test.ts:

import logger, { Request } from './';
import Analytics from 'analytics-node';

jest.mock('analytics-node');
const mockAnalytics = Analytics as jest.MockedClass<typeof Analytics>;

describe('Logger tests', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  it(`Should call analytics.track`, () => {
    const WRITE_KEY = process.env.WRITE_KEY;
    process.env.WRITE_KEY = 'test key';
    const request: Request = {
      userId: 23,
    };
    logger(request);
    expect(mockAnalytics).toBeCalledWith('test key');
    expect(mockAnalytics.mock.instances[0].track).toHaveBeenCalled();
    process.env.WRITE_KEY = WRITE_KEY;
  });
});

Result of unit test:

 PASS  examples/87654321/index.test.ts
  Logger tests
    ✓ Should call analytics.track (4 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.ts |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.954 s

Answer №2

When you include the

"@segment/analytics-next": "1.49.2"

let analyticsService: AnalyticsBrowser | null | undefined;

export const loadAnalyticsService = (props?: LoadAnalyticsProps): typeof analyticsService => {
    if ((analyticsService && analyticsService.instance?.initialized) || !props) {
        return analyticsService;
    }
    const { segmentAnalyticsKey, anonymousId, userId } = props;

    if (!segmentAnalyticsKey) {
        return null;
    }

    analyticsService = AnalyticsBrowser.load({ writeKey: segmentAnalyticsKey });
    analyticsService.setAnonymousId(anonymousId);
    analyticsService.identify(userId);

    return analyticsService;
};

Mocking the functions

const setAnonymousIdTest = jest.fn();
const identifyTest = jest.fn();

jest.mock("@segment/analytics-next", () => {
    const originalModule = jest.requireActual("@segment/analytics-next");

    return {
        __esModule: true,
        ...originalModule,
        AnalyticsBrowser: {
            load: () => {
                return {
                    setAnonymousId: setAnonymousIdTest,
                    identify: identifyTest,
                    instance: {
                        initialized: true
                    }
                };
            }
        }
    };
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for managing nested objects within React state and keeping them updated

Could someone kindly point out where I might be going wrong with this code? Upon checking the console.log, it seems like the date function is functioning correctly. However, despite using this.setState, the timestamp remains unchanged. Any help would be gr ...

Is there a way to incorporate an 'ended' feature into the .animate() function?

I'm currently working on a jQuery statement where I want to activate pointer events once the button animation is finished. However, I'm unsure how to incorporate an ended or complete function. Any suggestions on how to approach this? $('#ce ...

Arrange the JSONB data type with sequelize literal in your order

My current approach involves querying for all items using the following structure: const filteredItems = await allItems.findAll({ where: conditions, include: associations, order: sortingCriteria, limit: limit, o ...

Module lazily loaded fails to load in Internet Explorer 11

Encountering an issue in my Angular 7 application where two modules, txxxxx module and configuration module, are lazy loaded from the App Routing Module. The problem arises when attempting to navigate to the configuration module, as it throws an error stat ...

Having issues with the $addToSet method in my MongoDB API implementation

Despite searching through various topics, I couldn't find a solution to my specific problem. In an effort to enhance my JavaScript skills, I embarked on creating a quote generator. I successfully developed the API and frontend components. However, c ...

Ways to invoke a prop function from the setup method in Vue 3

I encountered the following code: HTML: <p @click="changeForm">Iniciar sesion</p> JS export default { name: "Register", props: { changeForm: Function, }, setup() { //How do I invoke the props change ...

Combining multiple meteor servers into a single database collection (local)

I'm currently working on a Meteor project that involves making multiple external API calls at different intervals and storing the retrieved data in MongoDB. My goal is to separate these API calls into individual "micro-services", each running on its ...

Is React failing to insert a span element in the client-side code?

First off, I'm a beginner in the world of react and recently tackled this guide on creating a real-time Twitter stream To cut to the chase, I wanted to include a link next to each tweet that followed its text. To achieve this, I simply added props to ...

Exploring the power of V-for with checkboxes in VueJS

One issue I am facing is related to triggering my save method only when a checkbox is selected or true in Vue JS. I have no problem listing values and saving them to the database using axios, Vue JS, and Spring Boot. However, every time I click the checkbo ...

What is the process for generating an alert box with protractor?

While conducting tests, I am attempting to trigger an alert pop-up box when transitioning my environment from testing to production while running scripts in Protractor. Can someone assist me with this? ...

Using Jquery to add new HTML content and assign an ID to a variable

Here is some javascript code that I am having trouble with: var newAmount = parseInt(amount) var price = data[0]['Product']['pris']; var id = data[0]['Product']['id']; var dat ...

Encountering an issue while attempting to extract an ACF field in WordPress using JavaScript

When I write the following code, everything works fine: <script> $(document).ready(function(){ var user_id = '<?php echo get_current_user_id(); ?>'; // This is working var subject = "<?php echo the_field('subject ...

What is the best approach to passing multiple variables to a JavaScript function in HTML/PHP?

Seeking help with calling a javascript function from PHP through embedded HTML code. Below is the script: <script> // THE FOLLOWING JAVASCRIPT FUNCTION REDIRECTS TO EDITUSER.PHP AND PASSES USERID VALUE. function startMaint(mo, yr){ ...

Module 'xlsx' cannot be located

I encountered this issue while building with Jenkins on the server, but it works fine on my local machine without any errors: 15:07:39 "", 15:07:39 "", 15:07:39 "ERROR in src/services/excel.service.ts:2:23 - error TS2307: Cannot find module 'xlsx&apos ...

When would you recommend setting localStorage in an Angular application?

In my EmployeesComponent, I have a list of employees with buttons for "Education Overview" and "Salary Overview" for each record. Clicking on an overview button takes me to the OverviewComponent, which then loads either the salary or education component. B ...

Tips for showing HTML content in an Angular UI grid

I've been attempting to showcase HTML within the grid by checking out this resource: Add html link in anyone of ng-grid However, my attempts led me to this code snippet: var app = angular.module('myApp', ['ngGrid']); ...

Is it true that TypeScript prohibits the presence of circular references under the condition of having generic parameters

I encountered an issue of type error in the given code snippet Type alias 'bar2' circularly references itself.ts(2456) type foo = { bars: bar[]; }; //works fine type bar = foo; type foo2<T extends Record<string, unknown> = Record< ...

Issue: Attempting to access the `userName` property on an undefined object (`tem`), resulting in a TypeError while using flalist

A concern has arisen with the React Native Flatlist as it fails to render properly. What steps should be taken in this scenario? Below is the code snippet for reference: Image description available here import React, {useState, useEffect} from 'react ...

Issue with Angular 18 component not being displayed or identified

Recently, I began building an Angular 18 application and encountered an issue with adding my first component as a standalone. It appears that the app is not recognizing my component properly, as it does not display when added as an HTML tag in my app.compo ...

Endless Google Locations Instances

My database entries are being displayed in a table using input fields. I want the Location field for each entry to be automatically completed using Google's API. HTML: <input name="edit_airplane[1][location]" type="text" class="airplane_location" ...