Xstate: [@xstate/test] Missing config for `done.invoke.fetch`

Created on 10 Sep 2019  ยท  7Comments  ยท  Source: davidkpiano/xstate

Bug or feature request?

Bug?

Description:

When model-testing a machine with a Promise service and providing the test model with all explicit events, the tests fail with the message Missing config for event "done.invoke.fetch". Providing a blank event implementation allows the test to continue, but they never get past the state that invokes that service.

(Bug) Expected result:

Tests don't fail.

(Bug) Actual result:

Tests fail.

(Bug) Potential fix:

I would anticipate that internal service invocation events aren't required to be supplied to the test model. There is no meaningful action I can think of that will map to that event.

Link to reproduction or proof-of-concept:

https://codesandbox.io/s/xstate-missing-config-repro-i4xls

You'll have to click the server rack icon on the left side and run the yarn test script to see this in action.

bug โš—๏ธ @xstattest

All 7 comments

I've experienced exactly the same issue.

The repo is: https://github.com/metamn/react-best-practices/tree/master/src/components/State

The message is:

 FAIL  src/components/State/State.test.js
  StateWithMachine
    โœ• coverage (1ms)
    reaches state: "closed" 
      โœ“ via  (32ms)
    reaches state: "opening" 
      โœ“ via OPEN (17ms)
    reaches state: "closing" 
      โœ“ via OPEN โ†’ CLOSE (10ms)
      โœ• via OPEN โ†’ done.invoke.openMenu โ†’ CLOSE (53ms)
    reaches state: "open" 
      โœ• via OPEN โ†’ done.invoke.openMenu (9ms)

  โ— StateWithMachine โ€บ reaches state: "closing"  โ€บ via OPEN โ†’ done.invoke.openMenu โ†’ CLOSE

    Missing config for event "done.invoke.openMenu".
    Path:
        State: "closed" 
        Event: {"type":"OPEN"}

        State: "opening" 
        Event: {"type":"done.invoke.openMenu"}

        State: "closing" 

      at TestModel.Object.<anonymous>.TestModel.getEventExecutor (node_modules/@xstate/test/src/index.ts:259:13)
      at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:270:27)
      at step (node_modules/@xstate/test/lib/index.js:43:23)
      at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
      at node_modules/@xstate/test/lib/index.js:18:71
      at Object.<anonymous>.__awaiter (node_modules/@xstate/test/lib/index.js:14:12)
      at TestModel.Object.<anonymous>.TestModel.executeEvent (node_modules/@xstate/test/lib/index.js:342:16)
      at Object.exec (node_modules/@xstate/test/src/index.ts:108:39)
      at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:157:33)
      at step (node_modules/@xstate/test/lib/index.js:43:23)
      at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
      at fulfilled (node_modules/@xstate/test/lib/index.js:15:58)

  โ— StateWithMachine โ€บ reaches state: "open"  โ€บ via OPEN โ†’ done.invoke.openMenu

    Missing config for event "done.invoke.openMenu".
    Path:
        State: "closed" 
        Event: {"type":"OPEN"}

        State: "opening" 
        Event: {"type":"done.invoke.openMenu"}

        State: "open" 

      at TestModel.Object.<anonymous>.TestModel.getEventExecutor (node_modules/@xstate/test/src/index.ts:259:13)
      at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:270:27)
      at step (node_modules/@xstate/test/lib/index.js:43:23)
      at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
      at node_modules/@xstate/test/lib/index.js:18:71
      at Object.<anonymous>.__awaiter (node_modules/@xstate/test/lib/index.js:14:12)
      at TestModel.Object.<anonymous>.TestModel.executeEvent (node_modules/@xstate/test/lib/index.js:342:16)
      at Object.exec (node_modules/@xstate/test/src/index.ts:108:39)
      at TestModel.<anonymous> (node_modules/@xstate/test/src/index.ts:157:33)
      at step (node_modules/@xstate/test/lib/index.js:43:23)
      at Object.next (node_modules/@xstate/test/lib/index.js:24:53)
      at fulfilled (node_modules/@xstate/test/lib/index.js:15:58)

  โ— StateWithMachine โ€บ coverage

    Missing coverage for state nodes:
        (machine).open

      123 |    */
      124 |   it("coverage", () => {
    > 125 |     testModel.testCoverage();
          |               ^
      126 |   });
      127 | });
      128 | 

      at TestModel.Object.<anonymous>.TestModel.testCoverage (node_modules/@xstate/test/src/index.ts:300:13)
      at Object.testCoverage (src/components/State/State.test.js:125:15)

Test Suites: 1 failed, 1 total
Tests:       3 failed, 3 passed, 6 total
Snapshots:   0 total
Time:        1.762s, estimated 2s
Ran all test suites related to changed files.

Watch Usage: Press w to show more.

Thanks a lot!

Thanks for the repro, gonna look into this.

By the way, for testing, you probably do not need to invoke services since instead you should model the services being invoked as states, where an event that transitions to a "done" state would be the resolution of that imaginary "service" (e.g., polling to wait that a loading indicator is no longer showing), so this has workarounds.

But I think the best solution would be to _not_ error when event configuration for built-in events is missing.

you probably do not need to invoke services since instead you should model the services being invoked as states

I'm having trouble wrapping my head around that. It seems like I'd have to change the machine definition to make that work, but only in test environments. Can you provide a small example of what you mean?

A "test machine" could use such a strategy to implement "event executor".

The problem here (as per @metamn repro) is slightly different though. It tests an "implementation machine" which invokes external~ service and from what I understand in such case you don't want to provide an executor for that done event. But rather test model should be able to await that service "as normal".

It fails here:
https://github.com/davidkpiano/xstate/blob/6f883a1f9d5fe001d244e804d9c8b46b0fab0f5e/packages/xstate-test/src/index.ts#L177
because it's just a proxy to executeEvent:
https://github.com/davidkpiano/xstate/blob/6f883a1f9d5fe001d244e804d9c8b46b0fab0f5e/packages/xstate-test/src/index.ts#L128

So I think a solution (which would have to be implemented) would be to recognize those special events and await for service to be completed - and that if needed could be mocked externally to avoid real API requests in tests etc.

@davidkpiano is my understanding correct?

It tests an "implementation machine"

Yes, this is the important line. By using services and (assumedly) the real machine, you're testing the machine that actually implements the app, not the abstract machine that represents the behavior as a user would experience it. Big difference.

Yes, that's correct. From what we've discussed testing "implementation" machines might be OK in certain situations, right? And thus a created test model could support that.

I'm not sure though how to properly await invoked service right now though. Seems like we would have to recognize which ID has been invoked and await it through children/activities property of the machine?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

carloslfu picture carloslfu  ยท  3Comments

amelon picture amelon  ยท  3Comments

laurentpierson picture laurentpierson  ยท  3Comments

dakom picture dakom  ยท  3Comments

doup picture doup  ยท  3Comments