Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Require from the actual file using NormalModuleReplacementPlugin

I have a Storybook setup for which I need for my React component's children components to stop doing API calls. The setup is quite complex, and it is also irrelevant to the question, so I'll just say that I need the components to stop doing API calls.

My ultimate goal is to have the children component stay in a "loading state" forever, so mocking the server response not a solution here.

The approach that I came up with was to replace my Thunk action creators with a stubbed one. Similar to what we do on Jest unit tests

// note that I'm using redux ducks: https://github.com/erikras/ducks-modular-redux

jest.mock('./ducks/students');

Of course the above doesn't work since Storybook doesn't run on Jest. So my current approach is to use the NormalModuleReplacementPlugin to replace the real module ducks/students.js with a stubbed one ducks/stubs/students.js which contains the functions, but with an empty body:

// ./ducks/students.js
export const loadResources() = fetch('/resources');

export default (state, actions => {
  // reducer's body
}


// ./ducks/stubs/students.js
export const loadResources() = Promise.resolve();  // STUBBED

export default (state, actions => {
  // reducer's body
}

The problem is that I need only the thunk action creators to be stubbed, everything else in the file (other actions, and reducer) needs to be the same.

This are the approaches I have considered so far to fix this:

  • Copy/paste the rest of the actual file into the stubbed one. This wouldn't scale.
  • Attempting to use require.requireActual(). It turns out this is a Jest custom function so I can't use it on Storybook.

Ideally I could find a way to import everything from the actual module into the stubbed one, and export the stubbed functions and the rest of the real functions that I need.

Any ideas how can I access the actual module from the stubbed one when I'm using NormalModuleReplacementPlugin?

Update 1: 2019-07-08

Tarun suggestion about just mocking the fetch function and returning a new Promise() worked for the particular case of "indefinitely loading".

However, looking at the big picture, I still would rather just stubbing out all of the API calls, so that I can setup the stories by just modifying the redux state.

"But why can't you just mock the JSON response?" I hear you ask. The JSON response is not necessarily 1-to-1 mapping with the app domain model. We have mapper functions that takes care of the transformation.

I'd be better if the programmers could work and setup the test cases with just the domain model knowledge, and don't need to know the server response JSON structure. Needless to say, the app redux store structure is the domain model.

So I still need an answer on how to require from the actual file, when using NormalModuleReplacementPlugin.

like image 498
Christopher Francisco Avatar asked Jun 27 '19 16:06

Christopher Francisco


1 Answers

I haven't tested this, but you might be able to achieve what you're after with the Aggregating/re-exporting modules syntax and overwriting your loadResources() function.

To do this, import your actual module into ./ducks/stubs/students.js, export all from that module, then define/overwrite loadResources() and export it as well. You can then use the NormalModuleReplacementPlugin as normal and pass in your stub file as the newResource that will contain all of your actual module reducers/actions that you wanted to keep with the thunk overwritten and stubbed out:

//ducks.stubs.students.js

export * from './ducks/students.js';

//override students.loadResources() with our stub
//order matters as the override must come after 
//the export statement above
export const loadResources() = //some stubbed behavior;
//webpack.config.js

module.exports = {
  plugins: [
    new webpack.NormalModuleReplacementPlugin(
      /ducks\.students\.js/,
      './ducks.stubs.students.js'
    )
  ]
}

A couple of notes/caveats/gotchas with this solution:

  1. You might have to update your exports to use let vs. const (not a big deal)
  2. Per this issue, the export * from expression isn't supposed to handle default exports. As such, you might have to add export { default } from './ducks/students.js';. Of course, keep in mind that you won't be able to export a default function native to your stubs file (unless you're overriding the original default function with a stub of course).
like image 133
willascend Avatar answered Nov 04 '22 21:11

willascend