Centralizing GraphQL Mock Data to an Internal Route
Reducing the pain of mocking server data
The team I work with at Expedia Group™ wanted to make a simple change to an A/B test override used in BrowserStack tests. In doing so, we found an opportunity to improve the handling of GraphQL mock data. This idea started when our team noticed all of our BrowserStack tests were failing. A quick investigation showed that the tests were using an API different from production. Since we wanted our BrowserStack tests to mimic our production environment, we added an A/B test override to ensure all traffic was hitting the current production API. This was supposed to be a one or two line code change to switch traffic for the BrowserStack tests over but… we had a problem.
Most of our BrowserStack tests were still failing despite the fact that the new API should return (ideally) identical data from the previous version. I had expected maybe a few would fail due to some slight differences between the two services, but this number of failures — almost every test — indicated we were probably looking at a larger issue. An investigation showed that all the failing tests had one thing in common: they sourced mocked GraphQL data that we had previously used to drive the tests.
Looking into how we handled mock data with our previous API, I found we were using a hapi plugin to check for a boolean flag named
useMockData within the BrowserStack URL. If
useMockData was set to true, the plugin would take over execution and return the expected mock data. This plugin was set up to work with the route we discontinued, so it made sense that the tests were failing because we were trying to request data from the new route which was not set up for handling requests for mock data.
So how did the old way work?
As mentioned previously, the team built a hapi plugin to handle our mock GraphQL data. When working with hapi, you will have a place in your
Manifest.json to register your plugins. You can think of
Manifest.json as a centralized place where you can tell the browser all about your server such as different services, overrides, connections to certain ports, and a lot more. This is also the place you register the hapi plugins you want to be included. Below is a basic example of what a simple
Manifest.json with a hapi plugin might look like:
To better illustrate the connection between
Manifest.json and the actual hapi plugin, some sample code for the
example-plugin mentioned above is below:
The important thing to note in the code above is the use of the .takeover() method. What this does is it “takes over” execution and returns the mock data as the response. So the code above can be summed up as, if the condition to return mock data is true, return mock data.
Why not just swap the routes and keep the hapi plugin?
We tried that at first. I replaced the usage of our old route with the new route when requesting mock data. It seemed like the easiest solution, and I was able to get the tests passing locally, but there was still a problem. When I ran the tests through BrowserStack in the team’s Jenkins pipeline with the generated AWS URL that we use to automate testing in our pipeline, they still failed as before. Turns out the host name (the AWS URL in this case) wasn’t recognized by our API and we were getting a 404. We tried to set up CORS and send the request with a host name it would recognize. It turns out we weren’t able to make CORS requests to our new API. Rather than change the security settings of the new API for just this one case we decided to go back to the drawing board.
This lead us to our solution that would solve the problem and give us a better way for handling mock data: create a new route for mock data. This works out nicely, since whenever mock data is needed, the route handler returns the same mock data that the hapi plugin returned, but now we don’t have to jump through hoops using hapi.
How to implement
The first step is creating a new route. Let’s call it
/mock/fakeGraphqlHandler and set it up with a route handler. There are a bunch of different ways to go about this. You could alternatively expose an endpoint in your API to return the mock data when requested. But if you want to use a route handler an example is given below:
The final step is adding the actual mock data to the correct files that get returned when appropriate. However or whenever you need mock data, you can hit that route and it will return the mock data you have set up. That’s it!
In the end, the architecture of the implementation looks something like this:
Why implement this?
This turned out to be an ideal implementation for a few reasons:
- We can now easily centralize and access any mock data we need in a single route with a single call that specifies in the request payload which mock data we need.
- It no longer matters what API we use or if we want only mock data. If we want to switch traffic back over to the old API, all our tests will still pass.
- It removes the need for an additional hapi plugin in our application that was previously taking over control just to return mock data.
- It makes the process for accessing mock data more straightforward and developer friendly.
This can even be applied to use cases outside of BrowserStack tests. This route can be used to return mock data in the event that a live API call fails and you don’t want to render the page without data. Any kind of static data is a good candidate to be put behind this route so that it’s all centralized.