Previously in this series, I made a GraphQL API server that returns mock data for a (nonexistent) app that displays my favourite music releases.
Having mock data facilitates our API server's integration with other parts of design and development work, such as UI development, prototyping, and testing, even while the server's resolvers are still under development.
An example query and mock data from the previous post:
However, this mock data could do with some improvements, for example:
- The
track_number
field—indicating a song/track’s order in an album—could return a negative integer (impossible) or a very large one (how many albums do you know contains 91 tracks??). - All albums, tracks, and artists have the same mock names, "Album/Track Title" and Artist Name. We want to develop and test the UI with varying data, including artists with a very long name or songs with a very long title.
We are going to use a library called casual for our custom mock data.
LOOKING FOR CONTRIBUTORS
Installation
npm install casual
Usage
var casual = require('casual');
// Generate random sentence
// You don't need function call operator here
// because most of generators use properties mechanism
var sentence = casual.sentence;
// Generate random city name
var city = casual.city;
// Define custom generator
casual.define('point', function() {
return {
x: Math.random(),
y: Math.random()
};
});
// Generate random point
var point = casual.point;
// And so on..
Casual uses javascript properties for common generators so you don't need to use function call operator
Embedded generators
// Address
casual.country // 'United Kingdom'
casual.city // 'New Ortiz chester'
casual.zip(digits = {
…Table of Contents
- Installation
- Basic usage
- More basic usage examples
- Custom generators with casual.define
- Custom providers with casual.register_provider
- Localization
1. Installation
npm install casual # or yarn add casual
Import/require casual in your server app main script (eg. index.js
or app.js
).
const casual = require("casual")
2. Basic usage
Casual comes with dozens of built-in generators (in the form of properties and functions) that return dummy data for various common purposes such as user data, addresses, numbers, dates, copy text, etc.
In an Apollo GraphQL server, we pass a casual generator to our custom mocks object so it returns the value generated by casual.
My mocks object now looks like this:
const mocks = {
Artist: () => {
return { name: () => "Artist Name" };
},
// ...etc
}
Let's replace the dummy text "Artist Name" with casual.name
.
const mocks = {
Artist: () => {
- return { name: () => "Artist Name" };
+ return { name: () => casual.name };
},
// ...etc
}
The server now returns mock artist names such as...
"Ms. Timothy Lesch"
"Mrs. Napoleon Hayes"
"Dr. Alisha Cole"
"Mr. Celia Lockman"
Unusual artist names for sure 😁, but at least they are not all "Artist Name anymore.
3. More basic examples
Now let’s try other simple built-in generators.
const mocks = {
Artist: () => {
return { name: () => casual.name };
},
Album: () => {
return {
// ... other fields
+ release_date: () => casual.date()
};
},
Track: () => {
return {
// ... other fields
+ track_number: () => casual.integer(1, 12)
};
},
+ ExternalUrlObject: () => {
+ return { spotify: () => casual.url };
+ },
}
-
casual.date()
is a function that returns a date string, eg."2021-03-25"
. It takes an optional argument, a momentjs-compatible formatter. If no argument is passed, it uses the default format'YYYY-MM-DD'
. -
casual.integer(1,12)
is a function that returns an integer between1
and12
, thus solving our track number predicament. (Yes, we could roll our own random integer generator without a library, but this is helpful in terms of speed.) -
casual.url
is a property that returns an URL, eg.'http://www.Germaine.net/'
. For some reason, the domain name is always capitalized, so I add.toLowerCase()
in my full code.
Combining generators
We can also combine generator functions. For instance:
-
casual.words(5)
returns five random words -
casual.integer(1,6)
returns a random integer between1
and6
We can use casual.words(casual.integer(1, 6))
to return a phrase that consists of one to six words. This gives us a (slightly) better control of the text length compared to generators like title
, text
, and description
.
4. Custom generators with casual.define
We can define custom generators with casual.define(name, fn)
. The first argument is the generator name and the second is a function returning the value.
Let's make a custom artist_name
generator.
casual.define('artist_name', () => {
// TODO return something
});
I want the mock artist name to return either a person’s name (representing solo artists) or a group/band name. I’ve got a list of fictional band names from Band Name Generator for the latter, and for the person names I'm simply using the casual.full_name
generator.
I define the band names in a hardcoded array:
const bandNames = [
"Crystal Greener",
"The Threshold Renewable",
"Unevenly Optimist",
// ... etc
];
Next I’m going to: (1) pick one name out of the bandNames
array, and (2) randomize returning either a person name or a band name. The casual.random_element()
helper, which returns an item from the array passed as argument, is perfect for those.
casual.define("artist_name", () => {
// pass array containing a person full_name and a band name to return at random
return casual.random_element([
casual.full_name,
casual.random_element(bandNames) // get a random band name
]);
});
Then we use it in our mock resolver function.
Artist: () => {
+ return { name: () => casual.artist_name };
},
When we run our query, now we get a list of more realistic artist names. 🎉
"Price Tillman"
"The Out Garter"
"Colby Bergnaum"
"The Threshold Renewable"
"Raphael Kohler"
"The Fountains Altars"
We can go beyond an individual field (eg. name) and define an object type in the same way, for example define a casual.artist
generator that returns an object with name
and bio
properties. Make sure the object shape matches the type definition, though.
casual.define("artist", () => {
return {
name: () => casual.artist_name,
bio: () => casual.description
}
})
5. Custom providers with casual.register_provider
Under the hood, casual generators are simply functions in a provider object. Casual includes the following built-in providers:
- address
- color
- date
- internet
- payment
- person
- misc
- number
- text
The full_name
generator, for example, is defined in the person
provider (source code).
casual.define
is sufficient for one-off individual generators, but if we have an object type with multiple fields/properties to mock up, we can register our own custom providers.
For example, a music track would not only have a name/title but also track number, tempo in BPM (beats per minute), and duration in millisecond. Let's try registering a custom music_provider
. First we create an object and pass it to casual.register_provider()
.
const music_provider = {
// TODO add functions or properties here
};
casual.register_provider(music_provider);
Then we add the generator functions as the object properties. For example, we're adding track_number
and tempo
to our music provider.
const music_provider = {
// Track number in an album, between 1 to 12
track_number: () => casual.integer(1, 12),
// Song tempo in BPM, between 60 to 180
tempo: () => casual.integer(60, 180)
};
casual.register_provider(music_provider);
Since these are regular functions, we can define our generator to accept arguments. For example, we can make an artist_name
generator that accepts a type
argument of "PERSON"
or "GROUP"
. If no argument is provided, we return any of those at random.
const bandNames = [ ... ]; // same data as Step 4
const music_provider = {
// ... other generators
// Return an artist name. Name can be a solo artist's name ("PERSON") or a band name ("GROUP").
artist_name: (type) => {
if (type === "PERSON") return casual.full_name;
else if (type === "GROUP") return casual.random_element(bandNames);
return casual.random_element([
casual.full_name,
casual.random_element(bandNames)
])
},
};
The best way to see what we can do when defining providers is to look at the source code, eg. the address provider.
To use the generators from our custom provider, we call them the same way as built-in or custom defined ones.
const mocks = {
Artist: () => {
return { name: () => casual.artist_name };
},
Track: () => {
return {
track_number: () => casual.track_number,
tempo: () => casual.tempo,
};
},
}
...
6. Localization
Casual comes with localization built-in. Simply add the locale name when calling the default import. For example, to get the Indonesian locale:
- const casual = require('casual');
+ const casual = require('casual').id_ID;
Locale providers are simply copies of the default providers in directories with the locale names (en_US
, fr_FR
, id_ID
, etc) — see source. These locale providers are submitted by contributors.
<digression>
Off topic but interesting (to me): While looking at the locale providers, I learned that Russian female last names are the male last names + an a
in the end. I went on to Google the tennis athlete Anna Kournikova and sure enough, her father's name is Kournikov.
</digression>
When a localized provider is not available, it falls back to the default (English). For instance, the Indonesian locale only has the address
provider, so everything else uses the default.
We barely scratched the surface with casual. Check out their docs to see all available functionalities.
Enough mocking for now—in the next post I'm going to start learning about GraphQL server resolvers. Thank you for reading!
Top comments (0)