Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Can't properly setup async actions test on my app #48

Closed
chukcha-wtf opened this issue Jan 28, 2016 · 27 comments
Closed

[QUESTION] Can't properly setup async actions test on my app #48

chukcha-wtf opened this issue Jan 28, 2016 · 27 comments

Comments

@chukcha-wtf
Copy link

Hi, I was looking at your project to get basic ideas for testing my RN app with redux, redux-thunk and a set of async actions. Thanks for your great work I've been able to successfully test basic actions and components rendering, but can't make it work with promises.

My app is RN 0.18 application with the following package.json config:

{
  "name": "myApp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "react-native start",
    "test": "rm -rf ./node_modules/jest-cli/.haste_cache && jest --verbose",
    "test:watch": "npm run test -- --watch"
  },
  "jest": {
    "scriptPreprocessor": "jestSupport/scriptPreprocess.js",
    "setupEnvScriptFile": "node_modules/react-native/jestSupport/env.js",
    "testPathIgnorePatterns": [
      "/node_modules/",
      "packager/react-packager/src/Activity/"
    ],
    "testFileExtensions": [
      "js"
    ],
    "moduleFileExtensions": [
      "js"
    ],
    "unmockedModulePathPatterns": [
      "react",
      "react-addons-test-utils",
      "promise",
      "source-map",
      "key-mirror",
      "immutable",
      "fetch",
      "redux",
      "redux-thunk",
      "fbjs"
    ]
  },
  "dependencies": {
    "lodash": "^3.10.1",
    "react-native": "0.18.0",
    "react-native-android-statusbar": "^0.1.2",
    "react-native-fs": "1.1.0",
    "react-native-sqlite-storage": "^2.1.1",
    "react-native-vector-icons": "^1.0.0",
    "react-redux": "3.1.2",
    "react-timer-mixin": "^0.13.3",
    "redux": "^3.0.5",
    "redux-thunk": "^1.0.2"
  },
  "devDependencies": {
    "art": "^0.10.1",
    "babel-core": "^6.4.5",
    "babel-jest": "^6.0.1",
    "d3": "^3.5.12",
    "jest-cli": "^0.8.2",
    "moment": "^2.10.6",
    "react": "^0.14.3",
    "react-addons-test-utils": "^0.14.3"
  }
}

My tests are placed at the top directory like so:

. myApp
|-- index.android.js
|-- index.ios.js
|-- app/
|-- android/
|-- ios/
|-- __tests__
|  |-- actions
|    └-- appState-test.js
|-- mocks
|  └-- Store.js
└-- package.json

Store.js file looks exactly like the one you've got here and my appState-test.js file looks like:

'use strict';

jest.autoMockOff();

const mockStore = require('../../exampleStore/snowflakeStore').default;
const actions = require('../../app/actions/appState');

import * as types from '../../app/constants/actionTypes';

describe('appStateSnowflake', () => {
  pit('should fetchSMTH', () => {    
    const expectedActions = [
      {type: types.START_FETCH_SMTH},
      {type: types.END_FETCH_SMTH}
    ];

    const store = mockStore({}, expectedActions);
    return store.dispatch(actions.fetchSMTH());
  });
});

Where actions/appState.js looks following:

import * as types from '../constants/actionTypes';

function startFetchSMTH() {
  console.log('startFetchSMTH')
  return { type: types.START_FETCH_SMTH }
}

function endFetchSMTH() {
  console.log('endFetchSMTH')
  return { type: types.END_FETCH_SMTH }
}

function doSomething() {
  console.log('Doing smth')
  return Promise.resolve();
}

export function fetchSMTH() {
  console.log('fetchSMTH')

  return (dispatch) => {
    dispatch(startFetchSMTH());

    return doSomething().then(() => {
      console.log('Im here HELP ME!!!')
      dispatch(endFetchSMTH())
    });
  }
}

However when I'm running my tests, only START_FETCH_SMTH has been dispatched and then I'm getting timeout: timed out after 5000 msec waiting for something to happen.
As for me this config looks exactly like the one in snowflake (tests from snowflake are running well) but they still doesn't work and I can't figure out why. Will appreciate if someone can help me as there isn't much information about RN testing, especially with Jest.

If this is an inappropriate question/issue - feel free to close it.

Thanks.

@bartonhammond
Copy link
Owner

Glad you like the project!

I'm not sure you're getting the mocked store

const mockStore = require('../../exampleStore/snowflakeStore').default;

I point this out because your directory layout shows something different with mocks/store.js

You should console.log(mockStore) in the test to confirm mockStore is the mocked version.

You could also npm run test-chrome after putting a break point in the mockStore to confirm it's getting picked up by jest.

Otherwise, nothing jumps out at me.

Let me know what happens

@chukcha-wtf
Copy link
Author

Thanks for the response. Actually, I'm getting a store, it's just an object with getState() and dispatch() functions (I can see it in my console.log and can get console.logs from inside that store, it was just a typo in file path as I can't share the actual source code) but it still don't want to work with promises. I've also tried this setup from official redux documentation, they're using it instead of pit with some done callback, it also doesn't work as expected (nothing gets dispatched), however tests are marked as successfull - but that's not a solution.

Can you tell how'd you come to such store mock implementation? It's a bit different from redux example and I'm interested why are you using pit instead of it with callback?

@bartonhammond
Copy link
Owner

pit is for a promised test. To understand it better, you should try changing the pit to it and see what happens on the stock Snowflake repo.

Jest is built on top of Jasmine and the Jest API docs reference PIT with this: https://www.npmjs.com/package/jasmine-pit.

When writing my tests I had problems w/ the redux version of the store and so I modified it to get my tests working. I also add try/catch so I could have some idea of what failed.

@chukcha-wtf
Copy link
Author

Thank you, I've gone through jasmine documentation, and that's why I've asked, as redux test example is using it to test promises which is a bit strange for me. In my case, all actions are working well when running within the app itself, but when testing - everything stuck on doSomething() function.

@larubbio
Copy link
Contributor

I'm running into the same issue. Something in the upgrade from react-native 0.17.1 to 0.18 is causing these tests to fail. If you take the base snowflake repo and update the versions the tests will fail. Here are the changed packages:

react-native@0.17.0 --> react-native@0.18.1
fbjs-haste@0.3.4    --> <not installed>
react-haste@0.14.2  --> <not installed>
                    --> │ ├─┬ react-transform-hmr@1.0.1
                        │ │ ├─┬ global@4.3.0
                        │ │ │ ├─┬ min-document@2.18.0
                        │ │ │ │ └── dom-walk@0.1.1
                        │ │ │ └── process@0.5.2
                        │ │ └─┬ react-proxy@1.1.2
                        │ │   └── react-deep-force-update@1.0.1
react-redux@3.1.2   --> react-redux@4.1.1

I also noticed that the current implementation of the store should also check that all of the expectedActions are emitted. That is why changing from pit to it gets the test passing. The store receives the first action but not the second.

@bartonhammond
Copy link
Owner

Seems like every time RN upgrades they break all the Jest testing...

@larubbio
Copy link
Contributor

Well RN 0.19.0-rc1 is out (but the tests are still broken with it :) )

@bartonhammond
Copy link
Owner

Snowflake was just fixed 6 days ago for RN 17! And I didn't figure it out - some one else did. This is frustrating.

@larubbio
Copy link
Contributor

How do you set a breakpoint when testing? if I run npm run test-chrome it launches chrome and stops at the first line in jest.js but none of my code is loaded in chrome dev tools so I can't set any breakpoints. If I continue code starts to load but the test run finishes and the debugger disconnects.

Nevermind, setting --preload true in package.json let me set the breakpoint, but since the code was transpiled it's unreadable so the debugger seems useless.

@bartonhammond
Copy link
Owner

Set a break point in your source code before running npm run test-chrome

@bartonhammond
Copy link
Owner

@larubbio What steps do you use do to upgrade? I did npm update and RN did not change version

@larubbio
Copy link
Contributor

For me I manually upgraded the versions in packages.json to the latest versions. I also had to move to npm3 because of an issue with fbjs (facebook/react-native#2985 (comment))

I then had to make some other small changes mostly the way redux was required since the /native entrypoint was dropped.

@bartonhammond
Copy link
Owner

@larubbio I just removed all the package.json dependancies and dev-dependencies and ran command line for every required module.

My tests run fine now w/ RN 18.1

My dependencies:

  "dependencies": {
    "apsl-react-native-button": "git+https://git@github.com/bartonhammond/react-native-button.git",
    "immutable": "^3.7.6",
    "key-mirror": "^1.0.1",
    "react-native": "^0.18.1",
    "react-native-gifted-spinner": "0.0.3",
    "react-native-simple-store": "^0.1.0",
    "react-native-simpledialog-android": "^1.0.2",
    "react-native-tab-navigator": "^0.2.15",
    "react-native-vector-icons": "^1.1.0",
    "react-redux": "^4.1.1",
    "redux": "^3.1.2",
    "redux-thunk": "^1.0.3",
    "regenerator": "^0.8.42",
    "tcomb-form-native": "^0.3.3",
    "underscore": "^1.8.3",
    "validate.js": "^0.9.0"
  },
  "devDependencies": {
    "babel-core": "^6.4.5",
    "babel-jest": "^6.0.1",
    "docker": "^0.2.14",
    "istanbul": "^0.4.2",
    "jest-cli": "^0.8.2",
    "react": "^0.14.7",
    "react-addons-test-utils": "^0.14.7"
  }

Now to a pit test

I changed authActions-test.js to just this:

describe('authActions', () => {

  pit('should logout', () => {
    const expectedActions = [
      {type: LOGOUT_REQUEST},
      {type: LOGIN_STATE_REGISTER},
      {type: LOGOUT_SUCCESS},
      {type: SESSION_TOKEN_REQUEST},
      {type: SESSION_TOKEN_SUCCESS}
    ];
    const store = mockStore({}, expectedActions);
    return store.dispatch(actions.logout());
  });
});

Then I run npm test src/reducers/auth/__tests__/authActions-test.js
and get:

 PASS  src/reducers/auth/__tests__/authActions-test.js (0.839s)
authActions
  ✓ it should logout
1 test passed (1 total in 1 test suite, run time 2.522s)

Then, change the pit it runs too.

But now try this - notice the it and missing actions

  it('should logout', () => {
    const expectedActions = [
      {type: LOGOUT_REQUEST}
    ];
    const store = mockStore({}, expectedActions);
    return store.dispatch(actions.logout());
  });

That runs as:

 PASS  src/reducers/auth/__tests__/authActions-test.js (0.765s)
authActions
  ✓ it should logout
1 test passed (1 total in 1 test suite, run time 2.386s)

But that's not right as there are other actions that ran!

So change that it back to pit and rerun

  pit('should logout', () => {
    const expectedActions = [
      {type: LOGOUT_REQUEST}

    ];

    const store = mockStore({}, expectedActions);
    return store.dispatch(actions.logout());
  });

That fails, as it should!

Store.action Object { type: 'LOGIN_STATE_LOGIN' }
 FAIL  src/reducers/auth/__tests__/authActions-test.js (0.819s)
authActions
  ✕ it should logout

 FAIL  src/reducers/auth/__tests__/authActions-test.js (0.819s)
● authActions › it should logout
  - Error: TypeError: Cannot read property 'type' of undefined
        at dispatch (src/reducers/mocks/Store.js:52:7)
        at eval (node_modules/redux-thunk/lib/index.js:9:61)
        at dispatch (node_modules/redux/lib/applyMiddleware.js:46:8)
        at eval (src/reducers/auth/authActions.js:354:2148)
        at tryCallOne (node_modules/react-native/node_modules/promise/lib/core.js:37:8)
        at eval (node_modules/react-native/node_modules/promise/lib/core.js:123:9)
        at flush (node_modules/react-native/node_modules/promise/node_modules/asap/raw.js:50:21)
        at doNTCallback0 (node.js:419:9)
        at process._tickCallback (node.js:348:13)
1 test failed, 0 tests passed (1 total in 1 test suite, run time 2.42s)

The mock store can tell you when actions aren't run when they were expected to, but not tell you when more actions were run then expected actions.

@bartonhammond
Copy link
Owner

I get the following when running Xcode:

rror building DependencyGraph:
 Error: Naming collision detected: /Users/barton/projects/stargazers/temp/snowflake/node_modules/react-native/node_modules/fbjs/lib/CSSCore.js collides with /Users/barton/projects/stargazers/temp/snowflake/node_modules/react/node_modules/fbjs/lib/CSSCore.js
    at HasteMap._updateHasteMap (HasteMap.js:132:13)
    at HasteMap.js:103:28
    at tryCallOne (/Users/barton/projects/stargazers/temp/snowflake/node_modules/react-native/node_modules/promise/lib/core.js:37:12)
    at /Users/barton/projects/stargazers/temp/snowflake/node_modules/react-native/node_modules/promise/lib/core.js:123:15
    at flush (/Users/barton/projects/stargazers/temp/snowflake/node_modules/react-native/node_modules/promise/node_modules/asap/raw.js:50:29)
    at doNTCallback0 (node.js:419:9)
    at process._tickCallback (node.js:348:13)
Fri, 29 Jan 2016 00:16:32 GMT ReactNativePackager:SocketServer exit code: 1

    at terminate (SocketClient.js:59:17)
    at Socket.<anonymous> (SocketClient.js:74:37)
    at emitOne (events.js:77:13)
    at Socket.emit (events.js:169:7)
    at emitErrorNT (net.js:1253:8)
    at doNTCallback2 (node.js:441:9)
    at process._tickCallback (node.js:355:17)
Command /bin/sh failed with exit code 1

@larubbio
Copy link
Contributor

That error is why I had to move to npm3. It installs dependencies flat which removes the conflict. I think you might also be able to resolve it by directly installing fbjs.

@larubbio
Copy link
Contributor

I updated my versions to match yours but I still get the timeout.

I noticed the mock store will tell you if actions are raised that match the start of expectedActions, but not error out if more or fewer actions are raised. I was going to look at fixing that once I got my tests passing.

@bartonhammond
Copy link
Owner

That's not correct @larubbio - I documented that above quite clearly.

  • It errors when fewer actions are provided.
  • It does not error if extra actions are provided if those actions are appended to the expected ones or out of sequence
  • the tests must use pit

@bartonhammond
Copy link
Owner

@larubbio I don't know anything about npm3 - has ReactNative required this now?

@larubbio
Copy link
Contributor

Are you sure about that? Looking at the code it's not clear to me how it would error if there were more actions than expected. It looks like the store just compares the list in order against the actions that are raised, but never checks that the count of raised actions matches the count of expected.

@larubbio
Copy link
Contributor

I just meant version 3 of npm. RN doesn't require it, but the way it installs dependencies resolved the fbjs issue for me.

@larubbio
Copy link
Contributor

@bartonhammond When I say "errors" I mean it doesn't fail the test when it should. It doesn't crash.

@larubbio
Copy link
Contributor

@bartonhammond In a version still using RN 0.17.1 I changed the test to this:

  pit('should logout', () => {
    const expectedActions = [
      {type: LOGOUT_REQUEST},
      {type: LOGIN_STATE_REGISTER},
      {type: LOGOUT_SUCCESS},
      {type: SESSION_TOKEN_REQUEST},
      {type: SESSION_TOKEN_SUCCESS},
      {type: "GOODBYE"}
    ];

    const store = mockStore({}, expectedActions);
    return store.dispatch(actions.logout());
  });

and it passes even though the event ' {type: "GOODBYE"}' is never raised. The test should have failed.

@bartonhammond
Copy link
Owner

@larubbio that is right - the test should have failed. but if you moved that GOODBYE somewhere else in the sequence, it would fail

@bartonhammond
Copy link
Owner

Are you sure about that? Looking at the code it's not clear to me how it would error if there were more actions than expected. It looks like the store just compares the list in order against the actions that are raised, but never checks that the count of raised actions matches the count of expected.

It can tell when there are missing, but not when there are more, if the extra are appended to the original set

@bartonhammond
Copy link
Owner

@larubbio

I was going to look at fixing that once I got my tests passing.

That would be much appreciated. I've created a different issue, #52 to upgrade to RN 0.18 as separate issue.

My apologies if I confused anything - this has been a long long long day... ;)

@bartonhammond
Copy link
Owner

I upgraded today to RN 0.18.1
Please read the release notes.
I changed the pit to it, not sure I understand...
Also upgrade to npm3
Thanks for all the discussion and help.

@bartonhammond
Copy link
Owner

Most all tests are passing now, see release notes: https://github.com/bartonhammond/snowflake/releases/tag/0.1.2-alpha

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants