Testing React MobX store? Cypress made it easy!
--
This blog post assumes you’ve already installed Cypress, understand react jargons, and state management mechanism. Now, if you are a person who does not like reading, let me be your Heimdal, and open the portal to the source code.
The sweet tiny React app
No demo has ever been successful without the mighty ToDoList app. Don’t blame me, I have developer superstitions.
The app is fairly simple and comprises of basic components — a <Form/> component to receive user input, a <List/> component to display the user input and a task <ListCounter/> component to show real-time tasks in hand. The source codes of the components can be found here.
Fun fact! I am fairly lazy, so don't expect any fancy styles for the components.
The Mobx Flavour
MobX
is a scalable state management solution. If you are already frustrated with the heavy loads of boilerplates in Redux for your small scale app, give MobX a try. I am not at all saying boilerplate is the only stopper for you to choose redux. It's a great library too. It’s just me who gets frustrated easily! And hey, redux vs mobx is not in the scope of this article too, if you interested just throw your eyes on the epic fight.
to cut the long story short, MobX operates with observable values, an action that changes those values, and reactive functions, which respond to these changes.
Our store is fairly simple. It observes the item and takes actions based on user operations to the ToDo List. So, when a user updates the form, MobX will automatically keep track of the observable field (items) and will take an action (addItem/deleteItem) based on the user input. In the app entry point, we tie the app components with the store provider. The provider component saves our back from sending store instance to every single component as a prop manually.
Configure the Cypress component testing boilerplate
MobX decorators are a futuristic feature and make uses of babel transpiler to make the code browser readable. I have used a custom webpack configuration to add the specific babel-loader
rule.
@cypress/react library helps you test React components in isolation. It is one of the viable solutions for developing unit and integration testing. It mounts the target component(s) and executes tests in a browser context. Finally, there is a solution for running isolated tests much closer to the browser context.
Install the required dependencies -
npm i -D @cypress/react @cypress/webpack-dev-server html-webpack-plugin webpack-dev-server webpack
As we are using a custom webpack configuration, we need to pass the webpack and babel configuration at the time of invoking the cypress dev-derver
. Hurry up! Let’s configure the cypress/plugins/index.js
The last thing you need to specify is where the component tests are located. Cypress enforces us to write the component tests right at the folder where the components itself situated. This is what our cypress configuration looks like for this demo.
Test the store with Cypress
In a realistic scenario, you may need to validate the integration effects with one of the components with the store or the store in isolation. Think about a bad sprint, where the team responsible for developing the <Form/> component has not delivered the component (because of a wired blocker. I know, it happens!). But another team delivered the <ToDoLis/> component. You should write the integration tests for the store and the list component. Cypress, being a good friend, can hold your back here.
You can expose the store in the window instance of Cypress after mounting the component using @cypress/react.
Now, you have the access to the store instance with the cypress browser context.
cy.window().its('store')
You can invoke store specific actionable executables directly and MobX will update the store with observed changes.
You can also choose to fetch the computed
fields to assert against the taken actions.
Now, sit back, relax and let Cypress do all the magic -
you can clearly see we are validating the <List/> component outcomes by invoking MObX’s internal actions. There’s no <Form/> component at all. If it does not amaze you, what will buddy?
Can we snapshot test the Mobx store?
A snapshot test is a simple way to evaluate changes in nested objects. When the test runs the first time, it renders a given component and stores the output in a snapshot file that gets committed alongside the code. Each time the test is re-run, the new output is compared against the stored version. If the two versions differ, a diff
is displayed, and the user has the option to update the committed snapshot file with the new output.
Snapshot testing has taken the JavaScript unit testing world by storm. Snapshot testing can be a very handy tool to test the MobX store nested computed values quickly. This will save you from creating boilerplate codes and deep assertions in nested state objects.
I will leverage Meinaart van Straalen’s wonderful plugin cypress-snapshot-plugin for this demo.
As a first step, you need to add the plugin as a dev-dependency to your project
npm i -D cypress-snapshot-plugin
Load the plugin within the cypress/plugins/index.js
file -
Also, you need to refer to the plugin from the cypress/support/index.js
file -
import 'cypress-plugin-snapshots/commands';
Now, comes the tricky part, as we make use of cypress-webpack-dev server to bundle the app and run tests in the browser context, there are a couple of extra configurations you need to take to use this amazing plugin. If you moved to Webpack 5, you need to add a reference process/browser
from the appropriate plugins
part of webpack.config.js
and add browserify alias for crypto
.
Now you are all set! Let’s write the first snapshot test.
You can clearly observe the benefit brings in by the snapshot test. In a real-world large scale app, you will get to handle more complex nested objects and assert the correct outcomes. The snapshot mechanism will compare the object with the previously stored object. It’s fast and lifesaver.
Now, what happens if the state of the object gets updated?
Obviously, the test will fail. It validates the new state reference with the previously taken snapshot and there is a difference.
You have to validate if the difference is desired. If not, it’s time to switch off your cell phone and start the ever hated debugging session. But if the distinction is desired, you have to update the snapshot, manually!
Just joking! Cypress will help you managing the snapshot just with a click of a button. Click on the COMPARE SNAPSHOT
icon followed by update snapshot
. That's all, really.
The whole developer experience around the snapshot testing with cypress is real fun. Try it out, if you haven’t already.
Parking lot!
We are living in the open-source world and as a human being, it's our trait to find simpler and more impactful solutions for any problems. The point is, the MobX store can be tested in (n) a number of possible ways. But, if you love to live in the present and want to test the application and its essentials components much closer to the browser context, Cypress truly unleash the possibility for you. I would love to listen to your thought.
Happy Testing!