npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cjs-mock

v1.4.15

Published

NodeJS module mocking for CJS (CommonJS) modules for unit testing purposes.

Downloads

37,149

Readme

cjs-mock

build status SemVer License Static Badge

NodeJS module mocking for CJS (CommonJS) modules for unit testing purposes. Similar to proxyquire, but simpler and safer. Sponsored by Aeroview.

😃 Easy to use

  • Super simple & straightforward documentation
  • Powerful debugging utility
  • Built in Typescript support

🛡 Defensive & immutable mocking

  • Throws an error if any mocks are unused by module we are mocking
  • Module Cache for mocked modules are always deleted before and after, to minimize side effects and make behavior more predictable and approximate immutability

💪 Robust & reliable

  • Tiny codebase written in Typescript with only 1 tiny dependency

Example

isValidWord.ts

import {readFile} from 'fs/promises'; // we're going to mock this

export async function isValidWord(word: string) {
  const validWords = await getValidWords();
  return validWords.indexOf(word) !== -1;
}

async function getValidWords() {
  const contents = await readFile('./dict.txt', 'utf-8');
  return contents.split('\n');
}

isValidWord.spec.ts

import {test} from 'hoare';
import {mock} from 'cjs-mock';
import * as mod from './isValidWord'; // just used for type

const dict = ['dog', 'cat', 'fish'].join('\n');
const mockMod: typeof mod = mock('./isValidWord', {
    'fs/promises': {readFile: () => Promise.resolve(dict)},
});

test('valid word returns true', async (assert) => {
  const result = await mockMod.isValidWord('dog');
  assert.equal(result, true);
});

See more examples in docs/examples.md

Installation

npm i cjs-mock -D

API

mock(modulePath: string, mocks: any): module

Returns a module with Dependency Injection for modulePath, as specified by the mocks argument. As a side effect, the module cache is deleted for module specified by modulePath and all modules specified in mocks. This cache is deleted at the start and end of the function. This should not matter during unit testing, but would likely be a problem in a production environment.

You should pass as a string the same thing you would pass to an import statement or require, with the following caveats:

  1. Any relative paths be relative to the module being returned
  2. It must only be a direct dependency of that module. It will not work recursively, including for re-exported modules (ie, export * from 'foo').

This function throws if any of the modules or properties are not resolvable, or if there are any unused (not required/imported by the module specified in modulePath):

Error: Unable to find foo
Error: The following imports were unused in ./foo: 
        ./bar

This is a defensive measure to ensure that the mocks are being used as intended.

The mocked dependencies will only be resolved once, and the real (non-mocked) dependencies will be used for any subsequent imports/requires. This is to prevent the mocks from being used in other modules that import the same module.

However, this can also be a source of confusion.

To aid in debugging, you can set the environment variable CJS_MOCK_DEBUG=1 to see the order of module resolution and mocking.

Partial mocking

You can nest mock() for partial mocking of nested dependencies:

const m = mock('./foo', {
    '.': mock('./bar', {
        'bob': () => 'fake bob'
    })
});

DO NOT USE IN PRODUCTION

Just like for proxyquire and other mocking utilities, use of this utility is not recommended in production environments, for the following reasons:

  1. Mocking utilities (including this one) are typically designed for unit testing in a sandbox environment, not production code.
  2. It's easy to get the mock wrong (which is why we throw errors for unused mocks and offer debug utilities).
  3. It has side effects on the module cache, by clearing it. This can lead to some very unexpected behavior outside of a unit test.

Debugging

A debugging utility is included, for use when you are having a difficult time seeing the order of how things are getting imported, and if a mock has been substituted after a successful resolution & match.

To enable this mode, set this in your environment: CJS_MOCK_DEBUG=1.

Example output (truncated screenshot):

In this screenshot, we can see that 'lambdaconf' is being imported twice, once from ./getSupportedAwsRegions.ts (in which case they are getting the replacement mock), and ./updateAwsLogsDestinations.ts (in which case they are not getting the replacement mock). This is because the module cache is cleared after the first import, and the real module is used for the second import. So, we either forgot to mock lambdaconf in one of these modules, or one of our imports also imports lambdaconf and we need to mock that module as well.

This can be useful for debugging, to see if a mock is being used or not, and to see the order of module resolution.

Be warned, this may produce a metric ton of output. It's sometimes shocking just how many modules are required in a node project, including built-in modules. You may want to limit the output to just the relevant test by only running that test.

Support, feedback, and contributions

  • Star this repo if you like it!
  • Submit an issue with your problem, feature request or bug report
  • Issue a PR against main and request review. Make sure all tests pass and coverage is good.
  • Write about this project in your blog, tweet about it, or share it with your friends!

Sponsorship

Aeroview is a lightning-fast, developer-friendly, and AI-powered logging IDE. Get started for free at https://aeroview.io.

Want to sponsor this project? Reach out.

Other useful libraries

  • autorel: Automate semantic releases based on conventional commits. Similar to semantic-release but much simpler.
  • hoare: An easy-to-use, fast, and defensive JS/TS test runner designed to help you to write simple, readable, and maintainable tests.
  • jsout: A Syslog-compatible, small, and simple logger for Typescript/Javascript projects.
  • brek: A small, yet powerful typed and structured config library with lambda support for things like AWS Secrets Manager.
  • typura: Simple and extensible runtime input validation for TS/JS, written in TS, fried in batter.

License

MIT