web-dev-qa-db-ja.com

JestおよびReact Nativeでのモックプラットフォーム検出

私がテストしようとしているコードのいくつかは、例えばを使用してプラットフォームを検出します:

import { Platform } from 'react-native';
...

if (Platform.OS === 'Android') {
  ...
} else {
  ...
}

これをJestや他の何かでモックする賢明な方法はあるので、1回のテスト実行で両方のブランチをテストできますか?

それとも、それを切り離してプラットフォームをコンテキスト変数などに入れるスマートな方法ですか?テストを容易にするためにコードを再構築することは常に感じられますが、それはごまかしのようなものです。

18
Stuart Watt

これは私のために働いた(Jest 21.2.1、Enzyme 3.2.0)

jest.mock('Platform', () => {
    const Platform = require.requireActual('Platform');
    Platform.OS = 'Android';
    return Platform;
});

テストの先頭、または a beforeAll などに配置します。

20
Bramus

プラットフォームをモック設定する方法は、テストで直接設定するだけでした。

it('should only run for Android', () => {
  Platform.OS = 'Android'; // or 'ios'

  // For my use case this module was failing on iOS
  NativeModules.MyAndroidOnlyModule = {
    fetch: jest.fn(
      (url, event) => Promise.resolve(JSON.stringify(event.body))
    ),
  }; 
  return myParentFunction().then(() => {
    expect(NativeModules.MyAndroidOnlyModule.fetch.mock.calls.length).toBe(1);
    expect(fetch.mock.calls.length).toBe(0);
  });
});

これにより、テスト中にAndroidでのみ実行されるようにプラットフォームがセットアップされ、関数が特定の関数のみを呼び出していることが確認されます。プラットフォーム依存コンパイルでラップされた関数は次のようになりました:

export default function myParentFunction() {
  if (Platform.OS === 'ios') {
    return fetch();
  }
  return NativeModules.MyAndroidOnlyModule.fetch();
}

プラットフォームにiOSを設定し、もう1つをAndroidに設定するという2つの異なるテストを作成することをお勧めします。理想的には、機能には1つの責任のみを持たせる必要があります。最初のテストを実行し、プラットフォームを動的に設定し、テスト番号2をすべて1つの関数で実行します。

7
Boomer Rogers

異なるOSをモックしたい場合、他の答えは機能しません同じテストスイート内および1回のテスト実行で、別の方法があります。コードでPlatform.OSを直接使用する代わりに、どこかにヘルパー関数を定義して使用します。

export function getOS() {
  return Platform.OS;
}

この関数canをテストでモックすることができます。

import * as helpers from './helpers';

// ...

it('does something on Android', () => {
  jest.spyOn(helpers, 'getOS').mockImplementation(() => 'Android');
  // ...
}

it('does something else on iOS', () => {
  jest.spyOn(helpers, 'getOS').mockImplementation(() => 'ios');
  // ...
}

このアイデアの功績は このGitHubの問題のコメント にあります。

4
d0gb3r7

これが必要なモックです:

const mockPlatform = OS => {    
  jest.resetModules();  
  jest.doMock("Platform", () => ({ OS, select: objs => objs[OS] }));
};

それを使用すると、次のことができます。

it("my test on Android", () => {
  mockPlatform("Android");
});

it("my test on iOS", () => {
  mockPlatform("ios");
});

そうすれば、両方のプラットフォームのテストを行うことができます

2
Estevão Lucas

「インポート」メソッドの問題かもしれません。これをチェックしてください:

const isAndroid = require('app/helpers/is_Android');

//import isAndroid from 'app/helpers/is_Android'

「インポート」では、これは機能しません。「必須」を使用する必要があります。

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

it("should be true when Android", () => {
  jest.mock('Platform', () => {
    return { OS: 'Android' };
  });

  expect(isAndroid).toBe(true);
});   
1
user10124150

このgithubの問題のソリューションを使用しています https://github.com/facebook/jest/issues/1370#issuecomment-352597475

Jest構成をpackage.jsonから別のファイルに移動しました。これまでのところ、以下を含むすべてがうまく機能しているようです:a)適切なファイルがプラットフォームに従ってインポートされます。たとえば、iosの場合:.ios.tsx、.native.tsx、.tsx b)PLATFORM.IOSは、test-iosの実行時にtrueを返します。何もモックする必要はありません

// package.json
"scripts": {
  "test": "cross-env NODE_ENV=test jest --config config/jest.desktop.json",
  "test-ios": "cross-env NODE_ENV=test jest --config config/jest.ios.json",
  "test-Android": "cross-env NODE_ENV=test jest --config config/jest.Android.json"
}

// config/jest.web.json
{
...
}

// config/jest.ios.json
{
...
  "preset": "react-native",
  "haste": {
    "defaultPlatform": "ios",
    "platforms": [
      "Android",
      "ios",
      "native"
    ],
    "providesModuleNodeModules": [
      "react-native"
    ]
  },
}

// config/jest.Android.json
{
...
  "preset": "react-native",
  "haste": {
    "defaultPlatform": "Android",
    "platforms": [
      "Android",
      "ios",
      "native"
    ],
    "providesModuleNodeModules": [
      "react-native"
    ]
  },
}
1
user3526468

jest.doMock および jest.resetModules を使用します

jest.resetModules()
jest.doMock('react-native', () => ({ Platform: { OS: 'Android' }}))
1
jkvim

React-Nativeから好きなものを次のようにモックできます。

describe('notifications actions tests', () => {
  let Platform;


  beforeEach(() => {
    jest.mock('react-native', () => ({
          Platform: {
           ... 
        }));


    Platform = require('react-native').Platform; // incase u would like to refer to Platform in your tests
  });
0
gran33
import React from "react";
import renderer from "react-test-renderer";
import SmartText from "../SmartText";

describe("markdown smart text component", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  it("renders with props on ios", () => {
    jest.mock("Platform", () => {
      return { OS: "ios" };
    });
    expect(
      renderer.create(<SmartText title="code ios" code />).toJSON()
    ).toMatchSnapshot();
  });

  it("renders with props on Android", () => {
    jest.mock("Platform", () => {
      return { OS: "Android" };
    });
    expect(
      renderer.create(<SmartText title="code Android" code />).toJSON()
    ).toMatchSnapshot();
  });
});
0
ethanneff