Jest snapshot test failing due to react navigation generated key

与世无争的帅哥 提交于 2019-12-14 03:52:34

问题


My Jest test is failing because of the timestamp in the key being different every time the test is run:

 FAIL  ./App.test.js
  ✕ renders without crashing (72ms)

  ● renders without crashing

expect(value).toMatchSnapshot()

Received value does not match stored snapshot 1.

- Snapshot
+ Received

@@ -347,11 +347,11 @@
              "index": 0,
              "isTransitioning": false,
              "key": "StackRouterRoot",
              "routes": Array [
                Object {
-                 "key": "id-1519567169760-0",
+                 "key": "id-1519567814666-0",
                  "routeName": "Menu",
                },
              ],
            },
          }

Here is my App.js file:

import React from 'react';
import { StackNavigator } from 'react-navigation';
import Menu from './components/Menu';
import List from './components/List';

const RootStack = StackNavigator(
  {
    Menu: {
      screen: Menu,
    },
    List: {
      screen: List,
    },
  },
  {
    initialRouteName: 'Menu',
  }
);

export default class App extends React.Component {
   render() {
     return <RootStack />;
   }
}

Here is my test file:

import React from 'react';
import App from './App';

import renderer from 'react-test-renderer';

test('renders without crashing', () => {
  const rendered = renderer.create(<App />).toJSON();
  expect(rendered).toBeTruthy();
  expect(rendered).toMatchSnapshot();
});

Is it possible override the key value or is there a way to ignore key when the test runs?


回答1:


Newer versions of jest also support property matchers which allow certain parts of the snapshot to be evaluated using a matcher instead of requiring the entire snapshot to be identical.

Here is an example showing a few different approaches to using this feature to solve OP's question:

it('Should ignore route keys in snapshot', () => {
  const testObject = {
    index: 0,
    isTransitioning: false,
    key: 'StackRouterRoot',
    routes: [
      {
        key: `id-${Date.now()}-0`,
        routeName: 'Menu',
      },
      {
        key: `id-${Date.now()}-1`,
        routeName: 'Splash',
      },
    ],
  };

  expect(testObject).toMatchSnapshot({
    routes: [
      {
        key: expect.stringMatching(/id-\d+?-\d/),
        routeName: 'Menu',
      },
      {
        key: expect.stringMatching(/id-\d+?-\d/),
        routeName: 'Splash',
      },
    ],
  }, 'Explicitly list all routes');

  expect(testObject).toMatchSnapshot({
    routes: expect.arrayContaining([
      expect.objectContaining({
        key: expect.stringMatching(/id-\d+?-\d/),
      }),
    ]),
  }, 'Match any route');
});

Which produces the following snapshots:

exports[`<App /> Should ignore route keys in snapshot: Explicitly list all routes 1`] = `
Object {
  "index": 0,
  "isTransitioning": false,
  "key": "StackRouterRoot",
  "routes": Array [
    Object {
      "key": StringMatching /id-\\\\d\\+\\?-\\\\d/,
      "routeName": "Menu",
    },
    Object {
      "key": StringMatching /id-\\\\d\\+\\?-\\\\d/,
      "routeName": "Splash",
    },
  ],
}
`;

exports[`<App /> Should ignore route keys in snapshot: Match any route 1`] = `
Object {
  "index": 0,
  "isTransitioning": false,
  "key": "StackRouterRoot",
  "routes": ArrayContaining [
    ObjectContaining {
      "key": StringMatching /id-\\\\d\\+\\?-\\\\d/,
    },
  ],
}
`;

Another jest feature I came across recently that might help here is custom snapshot serializers. Using this feature I think you could add a serializer for route objects that would remove the key or replace it with a static value before it is serialized. I can't provide an example right now as I have not yet used this feature, but it seemed worth mentioning.




回答2:


You can mock the data that is non-deterministic. https://facebook.github.io/jest/docs/en/snapshot-testing.html#tests-should-be-deterministic

For example, if you have a Clock component that uses Date.now(), the snapshot generated from this component will be different every time the test case is run. In this case we can mock the Date.now() method to return a consistent value every time the test is run:

Date.now = jest.fn(() => 1482363367071);

Now, every time the snapshot test case runs, Date.now() will return 1482363367071 consistently. This will result in the same snapshot being generated for this component regardless of when the test is run.




回答3:


You can override the key value by using a mocked function in your jest setup file: https://facebook.github.io/jest/docs/en/configuration.html#setupfiles-array

Set the key to always be the same by mocking Date.now to Date.now = jest.fn(() => 123) since under the hood react navigation uses this to generate their route keys: https://github.com/react-navigation/react-navigation/blob/master/src/routers/KeyGenerator.js




回答4:


I have found a completely different solution which I have put as an answer to my own similar question.

Snapshot test with Jest on nested react-navigation component - Object keys change so Snapshot match fails

I solved my problem, but not by mocking Date.now which was suggested in many other cases.

Instead I adapted an answer I found on https://github.com/react-navigation/react-navigation/issues/2269#issuecomment-369318490 by a user named joeybaker.

The rationale for this was given as:

The keys aren't really important for your testing purposes. What you really care about is the routes and the index.

His code is as follows and assumes the use of actions and reducers in Redux:

// keys are date and order-of-test based, so just removed them
const filterKeys = (state) => {
  if (state.routes) {
    return {
      ...state,
      routes: state.routes.map((route) => {
        const { key, ...others } = route
        return filterKeys(others)
      }),
    }
  }
  return state
}

it('clears all other routes', () => {
    const inputState = {}
    const action = { type: AUTH_LOGOUT_SUCCESS }
    const state = filterKeys(reducer(inputState, action))
    expect(state.routes).toBe........
})

I have adapted this for my case (I do not use Redux yet) as follows:

test("renders correctly", () => {

  const tree = renderer.create(<StackNavigator />);
  const instance = tree.getInstance();
  const state = filterKeys(instance.state.nav);

  expect(state).toMatchSnapshot();
});

// keys are date and order-of-test based, so just removed them
const filterKeys = (state) => {
  if (state.routes) {
    return {
      ...state,
      routes: state.routes.map((route) => {
        const { key, ...others } = route
        return filterKeys(others);
      }),
    }
  }
  return state;
};

The test that gets rendered looks like this:

// Jest Snapshot v1

exports[`renders correctly 1`] = `
Object {
  "index": 0,
  "isTransitioning": false,
  "key": "StackRouterRoot",
  "routes": Array [
    Object {
      "index": 0,
      "isTransitioning": false,
      "routeName": "FluidTransitionNavigator",
      "routes": Array [
        Object {
          "routeName": "SignInOrRegister",
        },
      ],
    },
  ],
}
`;


来源:https://stackoverflow.com/questions/48974583/jest-snapshot-test-failing-due-to-react-navigation-generated-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!