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

Examples of testing mobx stores with async actions #494

Closed
KatSick opened this issue Aug 19, 2016 · 10 comments
Closed

Examples of testing mobx stores with async actions #494

KatSick opened this issue Aug 19, 2016 · 10 comments

Comments

@KatSick
Copy link

KatSick commented Aug 19, 2016

Can you provide example (best practice) to write unit-tests for MobX store.
for example:

class Store {
@observable data = asMap({});

@action fetchData = () => {
  MyAPIPromise('fetch things async').then(response => data.set(response.id, response))
}

How should i unit test this store?
of course i mock my API bridge, however what should i use to check my 'data' hashmap.
thanks in advance

@andykog
Copy link
Member

andykog commented Aug 21, 2016

@ochervak, you mean, how to check if your 'data' is correct? You can use something like

const shallowEquals = require('shallow-equals')
// ...
const expectedData = { expectedKey: 'expectedValue' };
expect(shallowEquals(myStore.data.toJS(), expectedData)).toBe(true);

Other Map methods can be also handy.

@mweststrate
Copy link
Member

@ochervak I use when a lot in my unit tests to run the assertions at the proper moment (currently writing a blog post with some examples), but basically:

store.fetchData()
when(() => data.length > 0, () => { /* do assertions, end the test */ })

@KatSick
Copy link
Author

KatSick commented Aug 21, 2016

@mweststrate many thanks! i think issue can be closed

@KatSick
Copy link
Author

KatSick commented Aug 22, 2016

Now my test looks like:

it('should set active to a returned value on resolve', function(done) {
            TCStore.fetchActive();
            when(
                () => TCStore.data.has('active'),
                () => {
                    expect(TCStore.data.get('active')).to.not.equal(expectedResolve);
                    done();
                }
            );
        });

But there is a problem:

[mobx] An uncaught exception occurred while calculating your computed value, autorun or transformer. Or inside the render() method of an observer based React component. These functions should never throw e
xceptions as MobX will not always be able to recover from them. Please fix the error reported after this message or enable 'Pause on (caught) exceptions' in your debugger to find the root cause. In: 'When@
169'. For more details see https://github.com/mobxjs/mobx/issues/462  

So i need to wrap my assertions to setTimeout(()=>(), 0) to escape from mobx try\catch scope.

Can i fix this test without this ?

@mweststrate
Copy link
Member

@ochervak ah, I always use tape which doesn't throw on failed assertions :) In principle you can ignore the error, it's just a warning. I'll think about a proper solution for this!

@gryzzly
Copy link

gryzzly commented Nov 24, 2016

I’m using Jest and to get errors into the console when using when, I had to do the following:

it('sets initial values on init', function(done) {
    let Store = require('../Store').default;
    let instance = new Store();
    when(
      () => instance.isLoaded,
      () => {
        // async failing expects are not picked up with Jest,
        // you have to try/catch and call done.fail(e);
        try {
          expect(
            instance.bounds
          ).toEqual(
            '-122.93701171874999,37.01132594307015,' +
            '13.507690429687498,52.56675151567434'
          );
          done();
        } catch (e) {
          done.fail(e);
        }
      }
    );
  });

hope someone finds this useful.

thanks @mweststrate for helping me out on twitter.

@mweststrate
Copy link
Member

Actually in our own projects we use the following utility (see below). Not that this is not just useful for when (or mobx-util.whenWithTimeout, but for any asynchronous process.

So that you can do

when(
  () => expr
  catchErrors(done, () => {
    assertions
  }
)
/**
 * In async tests, JEST will die (in watch mode) if an exception is thrown from a callback. This utility will catch
 * the errors instead and report the test as failed in these case *
 *
 * @param {jest.DoneCallback} done
 * @param {T} callback
 * @returns {T}
 */
export function catchErrors<T extends Function>(done: jest.DoneCallback, callback: T): T {
	return function() {
		try {
			callback.apply(null, arguments)
		} catch (e) {
			done.fail(e)
		}
	} as any as T
}

cc: @cpojer is there a generic strategy for this in Jest? Didn't see anything in the docs

@cpojer
Copy link

cpojer commented Nov 29, 2016

I noticed this too and filed jestjs/jest#2059. We should definitely fix this in Jest.

@mweststrate
Copy link
Member

Thanks @cpojer! I added our typical workaround to the issue

@mweststrate
Copy link
Member

Closing for inactivity.

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

No branches or pull requests

5 participants