2

How to mock not installed npm package in jest?

I'm writing a library and I need to test some cases when optional dependencies are not installed.

Update

My library has an optional dependency. The end-user of my library can optionally to install styled-components.

In my tests (jest) I covered the case when styled-components is installed. Now I need to cover the case when the package is not installed.

test(`When styled-components is not installed`, () => {
  process.env.SC_NOT_INSTALLED = true
  const fn = () => {
    const styled = require(`./styled`)
  }
  expect(fn).toThrow(Error)
})
let styled

try {
  require.resolve(`styled-components`)
  styled = require(`styled-components`)

  if (process.env.NODE_ENV === `test` && process.env.SC_NOT_INSTALLED) {
    throw new Error(`Imitation styled-components is not installed`)
  }
}
catch {
  styled = () => {
    throw new Error(`Module not found: styled-components`)
  }
}

export default styled

process.env.SC_NOT_INSTALLED -> will not work because as I guess the test are running in different process.

sultan
  • 4,543
  • 2
  • 24
  • 32
  • 1
    Are you asking how to mock a library that isn't installed, or asking how to mock a library that *is* installed to look like it isn't. Please update your question with code and your test to give us something we can use to respond. – Brian Adams Mar 29 '19 at 07:30
  • @brian-lives-outdoors done. – sultan Mar 29 '19 at 08:43

1 Answers1

2

When an exception is thrown in your try you are exporting a function.

Calling the exported function is what throws the Error.

Change your test to this:

test(`When styled-components is not installed`, () => {
  process.env.SC_NOT_INSTALLED = true;
  const styled = require(`./styled`).default;
  expect(() => styled()).toThrow('Module not found: styled-components');  // Success!
});

...and it should work.


Update

If you are calling require('./styled') multiple times in the same test file, then you will want to add an afterEach that calls jest.resetModules, otherwise Jest will cache the module and just keep returning the same module for each require:

afterEach(() => {
  jest.resetModules();
})

test(`When styled-components is installed`, () => {
  const styled = require(`./styled`).default;
  // ...
});

test(`When styled-components is not installed`, () => {
  process.env.SC_NOT_INSTALLED = true;
  const styled = require(`./styled`).default;
  expect(() => styled()).toThrow('Module not found: styled-components');  // Success!
});
Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • No it is not that I meant. I need to check both sections of `try` and `catch`. Your snippet leads only first `try` section always. I want to test the `catch`. – sultan Mar 30 '19 at 06:36
  • The test in my answer tests the catch from the code in the question @sultan – Brian Adams Mar 30 '19 at 06:45
  • 1
    When an error is thrown in the `try` it drops into the `catch` and that returns a *function that throws an error when it is called*. So the test above causes the `catch` to run, then verifies that the function returned by the `catch` throws an error when it is called. Let me know if that makes sense and if you have any questions @sultan – Brian Adams Apr 01 '19 at 12:45
  • Thank you for helping to find solution @brian-lives-outdoors. Your given code is wrong. When you call const styled = require(`./styled`) you never rise error in catch section. First of all the env.variable not effecting other process. So the requires will be always success. – sultan Apr 01 '19 at 17:47
  • Setting the `process.env` variable is guaranteed to work since the module gets `required` by the test code within the same `process` (even when `Jest` is spawning multiple child processes), so that part is working. I did notice that I wasn't getting `styled` using the `default` export, so I've updated that above. I also updated the test to [specifically test for the error](https://jestjs.io/docs/en/expect#tothrowerror) thrown in the `catch` to demonstrate that it is working properly. @sultan – Brian Adams Apr 01 '19 at 20:15
  • 2
    Ahhh...I think I know why it hasn't been working for you. I bet you have a test for when it **is** installed earlier in the same test file and `Jest` is caching the module and returning the same one for when it **isn't** installed. I've updated my answer above with how to fix that. @sultan – Brian Adams Apr 01 '19 at 20:30