Test mobx-state-tree Models by Recording Snapshots or Patches

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 3 years ago

Testing models is straightforward. Especially because MST provides powerful tools to track exactly how your state changes over time. Track snapshots, action invocations or even patches to verify the correctness of your actions!

In this lesson you will learn:

  • To obtain immutable snapshots of the state using getSnapshot
  • To record snapshots using onSnapshot
  • To store and test modifications over time using onPatch
  • Using Jest's snapshot test feature to verify snapshots and patches
  • That MST can infer the type of a snapshot for you

Instructor: [00:00] We did now a lot of testing, but we didn't really leverage the mobx-state-tree type system yet. Let's start improving this just a little bit. For example, over here we're creating a new wish list item, but, in fact, we always know that these items are wish list items, because that is what we defined in the data model.

[00:22] Instead of creating a wish list item, we can just have mobx-state-tree do that for us. It just provides a snapshot of the wish list item and passes it to the [inaudible] . Mobx-state-tree with construct such a model for us. That means, even though we pass in plain JSON here, we can actually invoke the actions on the model instance. Basically, our data gets [inaudible] based on our models.

[00:54] Testing of all these properties is, of course, a bit cumbersome. What we can do instead is use the getSnapshot function, which is basically the inverse process of what creates does. Basically, we can say, "Hey, get me the state of the entire tree as a snapshot, as pure data, as JSON," and getSnapshot will produce an immutable data structure, which uses structural sharing, so it's very cheap.

[01:26] We can basically say that should be an object, which has items, which has an array, which has an entry, which has a name, and a test process, and we have done a deep equality check on the entire state of the wish list. Because of the fact that we're using gesture, we can also simply say that we expect it to match snapshots, which will record the snapshots for once and generate a snapshots file, which stores what our object looks like.

[02:13] To learn more about how this works, make sure to check out the gesture documentation. This is a really powerful feature.

[02:23] Snapshots are a very interesting feature. Snapshots basically are an immutable representation of the entire model tree, and they get generated automatically in the background. It's like you have this model tree, this list, and on the background, every time you change it, an immutable copy gets written. It might sound expensive, but actually it is not, because it uses structural sharing.

[02:51] Because we have all those snapshots available and we could store them, we can test this process by just looking at all the snapshots. We could basically be introducing an all snapshots function, so we'll create an array. Every time this list changes somehow and a new snapshot gets produced because it changed, it just pushes onto the states list.

[03:21] Now this list represents the entire states our list went through, throughout the test. Again, we can just take a look at the states and see how the snapshots evolve over time. We now created new snapshots, gestures recorded for the first run. Let's take a look at what it looks like.

[03:48] So [inaudible] test our list's transitions through to different states, but the interesting thing is that mobx-state-tree cannot only record through which states we went. It can also record which mutations we had. You can imagine that, at some point, you will have a very big model with lots of data in it which you might be testing.

[04:15] You're only interested in how, for example, this changed name affected your tree, without needing to snapshot all the rest of your state, which can be changing for other reasons. Besides listening to snapshots, we can also listen to patches.

[04:33] Every time an action changes something in the tree, a new JSON patch gets produced. A patch basically doesn't describe what the state is, but just what a mutation look like. It's an official standard, so it's also very powerful for communication with the backend or in collaborative spaces.

[05:00] We just copy this test, and now we start listening to the patches. This will produce some patches, and these are the patches that were created.

[05:14] A JSON patch is basically an operation at and above, so, "Where did it happen in a tree?" Those were the items. At position zero, this object was being added. After that, on the path of shared items, the name property was replaced with this value.

[05:38] Patches are a very powerful alternative mechanism to see how your state changes over time by not looking at the snapshots, not looking at the complete state, but just reading all of the mutations. Actually, patches can also be inverse applied to create redo and do behavior, but that's for later.

Gar Liu
Gar Liu
~ 3 years ago

Can the model intercept changes as a way to play animations? When the animation completes, then apply the model changes. I tried something like this:

addMiddleware(myModel, (call, next) => { // setTimeout is to simulate an animation. setTimeout(()=>{ next(call) }, 2000) });

But an error of: Error: [mobx-state-tree] Neither the next() nor the abort() callback within the middleware for the action: