#
Mock an existing service
This tutorial will gide you through the steps to create a mock for an existing service.
For this example, we'll mock the service projects-api.datacamp.com
.
#
Create a Poser server
If you don't have a poser server alreayd, let's create one:
yarn create @datacamp/poser-server
The code generator will ask you for a name, we'll choose mock-server
. This will create an empty poser server in a folder named mock-server
.
Next, let's go into that folder and install dependencies
cd mock-server
yarn install
We now have a server to run our service 🎉
#
Create a service mock
To create a service, we'll also use a generator. From the root of our mock server, we run the generator:
yarn create @datacamp/poser-service
The code generator will ask you for a name, well' choose projects-api
. It will then ask where to put this service, we'll keep the default services/
.
The code generator created a few files in services/projects-api
, let's import and register it:
import projectsApi from './services/projects-api';
poser.registerService('projects-api', projectsApi);
Let's make sure everything is working to this point by running the server:
yarn start
Visiting http://localhost:1000 should show projects-api
under "Registered services":
The generator includes a dummy schema you can interact with by visiting http://localhost:1000/projects-api/graphql. Let's now import the schema from the actual projects-api
service.
#
Fetch and use existing schema
The first step is to locate the graphQL endpoint. That endpoint must have introspection enabled.
The project's README lists the following endpoint: https://projects-api.datacamp-staging.com/api/internal-graphql/query
, that's the one we'll use.
It also says we'll need a "Basic auth" token. Each service may have different means of obtainig credentials, so we won't describe it in details here. We'll simply follow the steps in the README.
Now that we have a url and credentials, we can use Poser's command fetch-schema
to retrieve it:
Be sure to be connected to the VPN for this step.
yarn poser fetch-schema --url https://projects-api.datacamp-staging.com/api/internal-graphql/query --output ./services/projects-api/graphql/schema.graphql --headers '{"authorization": "Basic xxxxx"}'
Let's unpack this command:
yarn poser
we're executing the commandposer
throughyarn
fetch-schema
the name of the poser command--url
the url of the graphQL endpoint--output
where to write the schema locally--headers
since this endpoint requires basic auth, we provide anauthorization
header
We should now have an updated schema in services/projects-api/graphql/schema.graphql
Let's try our schema by visiting http://localhost:1000/projects-api/graphql
You should now be able to query this schema as if it was the original. GraphiQL (and any other graphQL client) will also display the documentation, as our endpoint poses as the real thing.
The full schema is auto mocked, let's try a query:
The mock api responds, but we'll need to tweak mocks and resolvers to get a more realistic response.
#
Tweak mocks and resolvers
There are a few things wrong with the current response to our query:
- We're fetching the project of id
1
, and yet we're getting back a project with a different id. technologyId
is not realistictimeNeeded
is a random string, but the reality is more something like"2 hours"
tasks
usually has more than 2, andtask.number
is not realistic (should be 1, 2, 3, ...)
#
Fix the id
Let's fix the id. To do this, we'll need to specify a custom resolver for Query.project
projects-api
does not use the standard Query
type. Instead that type is ProjectsApiSchema
. This is very unusual, most APIs will only use Query
.
export const resolvers: ResolverFn = (store) => {
return {
// aka "Query"
ProjectsApiSchema: {
project: (_, { id }) => {
return store.get('Project', id);
},
},
};
};
Let's try our query again, you should now get the same id
you requested 🎉
#
Fix technologyId
technologyId
normally matches a technology id from main-app (that's the service that owns technology ids). For the sake of simplicity, let's make it so our projects are either "R" (id=1) or "Python" (id=2).
This time we'll simply need to provide a mock for the field Project.technologyId
, this way anytime we try reading a technologyId
from a Project
, no matter from which query or mutation, we'll hit that mock.
import { faker, Mocks } from '@datacamp/poser';
export const mocks: Mocks = {
Project: {
technologyId: () => faker.random.arrayElement([1, 2]),
},
};
From now on, all Project.technologyId
will either be 1 or 2.
#
Fix timeNeeded
Fixing timeNeeded
is very similar to fixing technologyId
, we'll simply add a mock for Project.timeNeeded
:
timeNeeded: () => `${faker.datatype.number({ min: 2, max: 7 })} hours`,
#
Fix tasks
Finally, let's make some more realistic tasks. We'll return 10 of them and make sure the numbering matches:
For this we'll provide a custom Mock for Project.tasks
export const mocks: Mocks = {
Project: {
// ... other fields omitted for breveity
tasks: () => {
return utils.mockList(10).map((_, i) => ({ number: i + 1 }));
},
},
};
If we only wanted to change the length of the list, and not give a realistic value to number
, we could simply give use utils.mockList
as explained here.
That's it! We now have a pretty realistic response:
#
Customizing further
Is this response really realistic ? The answer is: it depends.
Let's take the example of timeNeeded
.
Originally, it's defined as a String
. We might be mocking this for a simple frontend that will simply display that text, probably next to a cute "clock" icon. If the only thing our app does is displaying the string to a user, there is no real value in mocking it. Any string would do, functionally speaking.
Of course, your UI may look strange, but is it what you need to test ?
However, if your app is actually going to parse that string to extract the time, then you definitely need to mock it realistically, as a random string would break the parser.
It really depends on your requirements and preferences, but one rule must be kept in mind: never mock more than you need. Mocks and resolvers will require maintenance from time to time, keeping them lean will help in the long run.