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

Testing with Jest #115

Closed
mohanzhang opened this issue Feb 9, 2015 · 13 comments
Closed

Testing with Jest #115

mohanzhang opened this issue Feb 9, 2015 · 13 comments

Comments

@mohanzhang
Copy link

Has anyone been able to do this successfully? It isn't entirely clear to me whether Marty should be auto-mocked as much as possible, or if in general it works better to not mock it. Any pointers would be appreciated, and I would be happy to contribute some documentation after I get it working.

@jhollingworth
Copy link
Contributor

We've found you don't need to mock much of Marty for unit testing.

For stores fetches we normally stub the specific method with sinon and return a fetch

var fetch = require("marty/fetch");

sinon.stub(UserStore, "getUser").returns(fetch.done({ username: "foo", ... }))

For action creators, we tend to just mock at the state source level.

sinon.stub(UserAPI, "createUser").returns(new Promise(user))

var UserActionCreators = Marty.createActionCreators({
  createUser: function (user) {
    UserAPI.createUser(user).then(onUserCreated)

    function onUserCreated(res) {
      ...
    }
  }
});

I'm going to be writing up some more notes about testing in the next week or two

@mohanzhang
Copy link
Author

Thanks James. I ended up getting things working by telling Jest not to mock underscore, util, and marty in package.json. Otherwise, there would be some cryptic error about toString() for a line in the uuid module.

Another error I saw in the process was Marty appearing to be undefined after Jest's automocking --- it turns out the reason here is because in index.js, Marty is assigned via _.extend, hence not mocking underscore. This issue on the jest github page provided the key insight: jestjs/jest#201

I definitely look forward some higher-level testing docs, though. Thanks for the good work on marty!

@dariocravero
Copy link
Contributor

Hey @mohanzhang, would you have some sample code around this? Say, package.json and some code being tested? Thanks :)

@Rodeoclash
Copy link

+1 to seeing this as well if you've got some time to write it up @mohanzhang

@mohanzhang
Copy link
Author

@dariocravero @Rodeoclash Hey guys, sorry it's taken me a while to get back to you. Unfortunately, I can't easily share code examples since this was for my work, but I think this bit of package.json should be helpful:

{
  [...],
  "jest": {
    "rootDir": "./app/assets/javascripts",
    "scriptPreprocessor": "<rootDir>/__tests__/preprocessor.js",
    "moduleFileExtensions": [
      "js",
      "coffee",
      "cjsx"
    ],
    "unmockedModulePathPatterns": [
      "react",
      "react/addons",
      "lodash",
      "jquery",
      "moment",
      "underscore",
      "util",
      "marty"
    ],
    "testFileExtensions": [
      "js",
      "coffee",
      "cjsx"
    ],
    "testPathIgnorePatterns": [
      "preprocessor.js"
    ]
  }
  [...],
}

And once you have Jest set up in this way (preprocessor.js is based on http://facebook.github.io/jest/docs/tutorial-react.html), your tests can be pretty straightforward. Here is an example testing a fictitious Store:

describe 'MyStore', ->
  # imports
  jest.dontMock('../my_store')
  MyStore = require('../my_store')

  React = require('react/addons')
  TestUtils = React.addons.TestUtils

  beforeEach () ->
    MyStore.setState(MyStore.getInitialState())

  describe '#foo', () ->
    it "bars real good", ->
      MyStore.bar(5)  # suppose for this example that this just sets the state, and .foo retrieves the same state
      expect(MyStore.foo()).toEqual(5)

If you set up package.json right, then all the marty stuff inside MyStore should "just work" when you run npm test.

@jhollingworth
Copy link
Contributor

This is awesome, I will add a testing guide to the website based on this

Thanks
James

On 18 Feb 2015, at 7:16 pm, Mohan Zhang [email protected] wrote:

@dariocravero @Rodeoclash Hey guys, sorry it's taken me a while to get back to you. Unfortunately, I can't easily share code examples since this was for my work, but I think this bit of package.json should be helpful:

{
[...],
"jest": {
"rootDir": "./app/assets/javascripts",
"scriptPreprocessor": "/tests/preprocessor.js",
"moduleFileExtensions": [
"js",
"coffee",
"cjsx"
],
"unmockedModulePathPatterns": [
"react",
"react/addons",
"lodash",
"jquery",
"moment",
"underscore",
"util",
"marty"
],
"testFileExtensions": [
"js",
"coffee",
"cjsx"
],
"testPathIgnorePatterns": [
"preprocessor.js"
]
}
[...],
}
And once you have Jest set up in this way (preprocessor.js is based on http://facebook.github.io/jest/docs/tutorial-react.html), your tests can be pretty straightforward. Here is an example testing a fictitious Store:

describe 'MyStore', ->

imports

jest.dontMock('../my_store')
MyStore = require('../my_store')

React = require('react/addons')
TestUtils = React.addons.TestUtils

beforeEach () ->
MyStore.setState(MyStore.getInitialState())

describe '#foo', () ->
it "bars real good", ->
MyStore.bar(5) # suppose for this example that this just sets the state, and .foo retrieves the same state
expect(MyStore.foo()).toEqual(5)
If you set up package.json right, then all the marty stuff inside MyStore should "just work" when you run npm test.


Reply to this email directly or view it on GitHub.

@dariocravero
Copy link
Contributor

Thanks @mohanzhang :) Looking forward to seeing this on the guides and contributing to it too

@dennis-johnson-dev
Copy link

This looks great. We're figuring out the Jest tests as well. Has anyone figured out how to test that the remotely part of a get request returns the right data? We would like to call Store.getSomeData() and have a mocked API response be returned. So it's got to go through locally, then remotely, then back in remotely. Any ideas?

@mohanzhang
Copy link
Author

@songawee The pattern we adopted involves following the facebook flux diagram pretty closely: https://github.com/facebook/flux

See that Web API offshoot from Action Creators? That's where we're doing all our API work. So in your Store.getSomeData() example, we actually have it so that the component requesting the update fires off an action ActionCreator.requestData(), which might look something like

requestData: () ->
  @dispatch() # in case the store needs to react to an async request having started
  MyAPI.getData()

And inside MyAPI.getData:

getData: () ->
  $.get('/endpoint.json', (result) ->
    SourceActionCreator.receiveData(result)
  )

What is SourceActionCreator? It's just another Action Creator, but it's limited to only those actions that are fired as a result of an API succeeding. The store can then listen to this "receiveData" action and do the appropriate thing.

I know this deviates a little bit from the marty docs, but we have found that marty does not really hinder this pattern in any way, and that it makes for easier testing. We just check that the request and receive actions have the right effects, and test the API portion separately (which we place under sources/ using MyAPI = Marty.createStateSource(...))

This effectively lets us decouple all our external data actions from the rest of our flux app, and we can trust that if the API source is well tested and that the rest of the app is well tested, given that marty is well tested, we can trust that it will all work as advertised. Otherwise, you end up testing bits of marty as well and who wants to test more than they have to, right? ;)

@Rodeoclash
Copy link

This is pretty much where I wound up as well. I had some trouble testing stores that relied on sources though. A sample test:

it('should create', function () {
    RegistrationsSource.create.mockImplementation(function (details) {
      return {
        'then': function (cb) {
          cb(serverResponse);
          return {
            'catch': function () {}
          }
        }
      }
    });
    store.create(createPayload);
    expect(RegistrationsSource.create).toBeCalledWith(createPayload);
  });

The is testing a create method on a store which calls a source which returns a promise. I wound up mocking the returned promise. I'm quite new to testing with Jest / Flux so in no way is this a suggestion for other people to use!

I think it would be good to start collaborating on a testing page for the documentation, some things I think that would be good in it:

  • Setting up Jest with Marty, including examples of package.json etc.
  • Examples of testing stores, in particular the interface between stores / sources and how they should be stubbed out.
  • Perhaps testing views that use MartyJS components (although so far it looks like standard React view testing, the MartJS components are not imparting any specific behaviour on them aside from the state mixin)

@dennis-johnson-dev
Copy link

Awesome, thanks @Rodeoclash and @mohanzhang for your responses. We're still looking into how to integrate the state source. Here is what we ended up with:

pit('returns data after an Api fetch', () => {
      return new Promise((resolve, reject) => {

        Api.query.mockImpl(() => Promise.resolve(Response));

        var listener = Store.addChangeListener(function() {
          var value = Store.getData();
          if (value.done) {
            expect(value.result).toBe('data');
            resolve();
            listener.dispose();
          }
        });

        Store.getData();
       });
    });

Initially our store is empty. We call Store.getData() to get the fetch started. The store actually emits two changes (one as the result of an API fetch that returns a promise and one that we call explicitly). We check the value of the fetch in the change listener and only do the expectations when the fetch is done.

@vid vid mentioned this issue Mar 11, 2015
@jhollingworth jhollingworth mentioned this issue Mar 29, 2015
@alexprice1
Copy link

Question:

How do you test individual react components that depend on Marty stores, when they have no reference to this.app?

Additionally, I am using require.context to register my Marty stores. Has anyone found a way to test this with jest?

@taion
Copy link
Member

taion commented Aug 10, 2015

@chapinkapa You need to set up the wrapper/container yourself. You'll need to use Webpack if you want require.context to work.

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

7 participants