react-native-mock icon indicating copy to clipboard operation
react-native-mock copied to clipboard

Mocking platform-agnostic requires

Open IanVS opened this issue 9 years ago • 11 comments
trafficstars

React Native does some magic around requires to allow require(img.png) as pointed out in https://github.com/lelandrichardson/react-native-mock/issues/11, but it also allows platform-specific components to be named with an extension .ios.js or .android.js, and then be imported with a simple import {MyComponent} from 'my-component.js'; (http://facebook.github.io/react-native/docs/platform-specific-code.html#platform-specific-extensions). This avoids the need to sprinkle Platform.OS ternaries throughout the code, but of course Node has no idea how to resolve the import/require and blows up.

Do you have any suggestions or ideas of the best way to handle this?

IanVS avatar Mar 02 '16 19:03 IanVS

I use an environment variable to set the platform, like APP_PLATFORM=android npm test. And in the code I do var platform = process.env.APP_PLATFORM || 'ios' and then use this variable during mocking a require or doing other platform-specific stuff. Another option is to have separate tests for each platform, but this leads to code doubling. Not sure which of these approaches is better.

React Native uses packager that dynamically builds js-bundles depending on provided parameters, like platform, dev, hot, that's why it's so flexible and power.

Jeiwan avatar Mar 03 '16 10:03 Jeiwan

Besides just using a load of conditionals to check, what you could do is change the way node requires js files, and check if there's a .android.js or .ios.js version and return that instead, which you could switch on an env var. Best library i've found to do it is node-hook, but I have no idea if it actually works, or plays nice with react-native-mock

RealOrangeOne avatar May 20 '16 15:05 RealOrangeOne

I just want to share something in case someone come through the problem of testing platform specific code.

Not related to the import themselves but I found a solution for platform specific code using the RN Platform module. I use mocha and as entry point for my tests I require this file /bin/test.js:

var ReactNativeMock = require('react-native-mock');

ReactNativeMock.Platform.OS = process.env.PLATFORM || 'ios';

require('react-native-mock/mock');

This way I don't have to change my code, I keep using the RN module and I can simulate the Platform with an env var. Unfortunately this doesn't work for imports as they are handled by the RN packager.

julien-rodrigues avatar Jul 20 '16 21:07 julien-rodrigues

@julien-rodrigues whilstt that is a nice solution, we already support something like this. You can use 'Platform.__setOS' to set the OS programatically during tests. This should make things much easier for you, and it's still possible to use process.env.

RealOrangeOne avatar Jul 21 '16 06:07 RealOrangeOne

I forgot to say that in my case I need to set stuffs outside of the render cycle. So I need to have the OS correctly set before anything else.

I am testing a component which style has a color property which is changing depending on the Platform.OS value.

export const selectionColor = Platform.OS === 'ios' ? Colors.lighterGrey : Colors.lighterGrey20;

I can't put it inside a RN Stylesheet as selectionColor is going to be used to fill the selectionColor prop of the RN TextInput component.

I could put the ternary inside the selectionColor prop of the TextInput and this would allow me to change the OS programatically in my tests as the check is done inside the render cycle. But I don't see the case where the Platform would change its OS when running the app. And for the sake of consistancy, all my component styles are in a separate file.

But wrapping it into a function would be a working solution tho. lol

And as for the __setOS method, seeing the __ made me think that I shouldn't be using that. But knowing that I can I'm going to change my test's entry point. Thanks for the hint!

julien-rodrigues avatar Jul 21 '16 13:07 julien-rodrigues

Another way to do it is to do it is by component basis. The issue with this is that you need to include the a boolean for Platform as a prop. While it adds a bit extra code . Its allows me to mock things in a contained manner.

Here is a webView component below as an example. I use it to conditionally create a vertical offset due to a navigation bar component. Would love to hear your feedback!

const SHWebView = ({ verticalOffset, uri, android }) => {
  const androidOffset = 53;
  const iosOffset = 64;
  const marginTop = 
    isNaN(verticalOffset) ? (android ? androidOffset: iosOffset) : verticalOffset;

  return (
    <View style={[styles.container, { marginTop }]}>
      <WebView source={{ uri }}/>
    </View>
  )
};

SHWebView.defaultProps = {
  android: Platform === 'android'
}

danielbh avatar Oct 27 '16 10:10 danielbh

@RealOrangeOne I am trying to set the platform to Android, using:Platform.__setOS('android');

Unfortunately it is not working, the component does not render. Is there another way to do it?

If I do Platform.__setOS('ios'); it does what is expected that is keep the iOS platform.

SandroMachado avatar Nov 29 '16 18:11 SandroMachado

@SandroMachado I'm going to need a lot more information than that. You have to re render you component completely after changing the OS. We also currently dont support using platforms in require statements, if that's what you meant

RealOrangeOne avatar Nov 29 '16 20:11 RealOrangeOne

Just pass it in as a default boolean prop!

Component.defaultProps { android: Platform.OS === 'android' }

Does that work for you?

On 29 Nov 2016, at 21:42, Jake Howard <[email protected]mailto:[email protected]> wrote:

@SandroMachadohttps://github.com/SandroMachado I'm going to need a lot more information than that. You have to re render you component completely after changing the OS. We also currently dont support using platforms in require statements, if that's what you meant

You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/RealOrangeOne/react-native-mock/issues/17#issuecomment-263692513, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AGWFMh2VPbd5ZDHmlON8Q_PY09j-1Ximks5rDI4sgaJpZM4Hnv1G.

danielbh avatar Nov 29 '16 20:11 danielbh

I think I found the issue, the problem is that the DatePickerAndroid is not mocked.

On 29 Nov 2016 20:50, "Daniel Hollcraft" [email protected] wrote:

Just pass it in as a default boolean prop!

Component.defaultProps { android: Platform.OS === 'android' }

Does that work for you?

On 29 Nov 2016, at 21:42, Jake Howard <[email protected]<mailto: [email protected]>> wrote:

@SandroMachadohttps://github.com/SandroMachado I'm going to need a lot more information than that. You have to re render you component completely after changing the OS. We also currently dont support using platforms in require statements, if that's what you meant

You are receiving this because you commented. Reply to this email directly, view it on GitHub<https://github.com/ RealOrangeOne/react-native-mock/issues/17#issuecomment-263692513>, or mute the thread<https://github.com/notifications/unsubscribe- auth/AGWFMh2VPbd5ZDHmlON8Q_PY09j-1Ximks5rDI4sgaJpZM4Hnv1G>.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/RealOrangeOne/react-native-mock/issues/17#issuecomment-263694708, or mute the thread https://github.com/notifications/unsubscribe-auth/AAmju3Yhh0prHjt9eZhCbCIMMWVC_PLhks5rDJAugaJpZM4Hnv1G .

SandroMachado avatar Nov 29 '16 20:11 SandroMachado

Curious if anyone reached a good solution for the import issue? Currently, I'm creating a .js file at test runtime and then deleting it after in my script. Talk about a hack 😝

esauter5 avatar Oct 31 '17 18:10 esauter5